web-dev-qa-db-ja.com

React-Native更新リストビューDataSource

反応ネイティブで作成しているiOSアプリがあります。 GameクラスにはListViewコンポーネントが含まれています。コンストラクターで状態を設定し、dataSourceを含めます。現在、別の状態プロパティ(this.state.ds)に保存するデータのハードコードされた配列があります。次に、componentDidMountcloneWithRowsメソッドを使用して、this.state.dsをビューのdataSourceとして複製します。 ListViewsの限りでは、これはかなり標準的であり、うまく機能しています。コードは次のとおりです。

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

 var React = require("react-native");
 var { StyleSheet, Text, View, ListView, TouchableHighlight } = React;

class Games extends React.Component {
constructor(props) {
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 != r2
    });
    this.state = {
        ds: [
            { AwayTeam: "TeamA", HomeTeam: "TeamB", Selection: "AwayTeam" },
            { AwayTeam: "TeamC", HomeTeam: "TeamD", Selection: "HomeTeam" }
        ],
        dataSource: ds
    };
}

componentDidMount() {
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(this.state.ds)
    });
}
pressRow(rowData) {
    var newDs = [];
    newDs = this.state.ds;
    newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(newDs)
    });
}

renderRow(rowData) {
    return (
        <TouchableHighlight
            onPress={() => this.pressRow(rowData)}
            underlayColor="#ddd"
        >
            <View style={styles.row}>
                <Text style={{ fontSize: 18 }}>
                    {rowData.AwayTeam} @ {rowData.HomeTeam}{" "}
                </Text>
                <View style={{ flex: 1 }}>
                    <Text style={styles.selectionText}>
                        {rowData[rowData.Selection]}
                    </Text>
                </View>
            </View>
        </TouchableHighlight>
    );
}
render() {
    return (
        <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRow.bind(this)}
        />
    );
}
}
var styles = StyleSheet.create({
 row: {
    flex: 1,
    flexDirection: "row",
    padding: 18,
    borderBottomWidth: 1,
    borderColor: "#d7d7d7"
},
selectionText: {
    fontSize: 15,
    paddingTop: 3,
    color: "#b5b5b5",
    textAlign: "right"
}
});


module.exports = Games

私が抱えている問題はpressRowメソッドにあります。ユーザーが行を押すと、選択が変更され、デバイスで変更がレンダリングされます。いくつかのデバッグを通じて、Selection配列のオブジェクトのnewDsプロパティを変更しているにもかかわらず、this.state.dsのオブジェクトの同じプロパティが変更され、this.state.dataSource._dataBlob.s1のオブジェクトも同様に変更されることに気付きました。さらにデバッグすることで、これらの他の配列が変更されたため、状態を設定してrowHasChangedが呼び出されたときに、複製している配列が配列this.state.dataSource._dataBlob.s1と一致するため、ListViewのDataSourceオブジェクトは変更を認識しません。そのため、変更のようには見えず、再レンダリングされません。

何か案は?

40
Brandon M

これを試して:

pressRow(rowData){

    var newDs = [];
    newDs = this.state.ds.slice();
    newDs[0].Selection = newDs[0] == "AwayTeam" ? "HomeTeam" : "AwayTeam";
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(newDs)
    })

}

これにより、配列のコピーが作成され、this.state.dsの元の配列とは無関係に変更できます。

18
eddyjs

私のように誰かがこの問題に出くわした場合、ここに完全な解決策があります。

基本的に、ListViewコンポーネントにはバグがあるようで、ListViewを再描画するには、データソースで変更する各アイテムを再構築する必要があります。

最初に、データソースと配列のコピーを作成し、状態に保存します。私の場合、foodsは配列です。

constructor(props){
    super(props);
    var ds = new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
    });
    this.state = {
        dataSource: ds.cloneWithRows(foods),
        db: foods,
    };
}

データソース内の何かを変更する場合は、状態に保存した配列のコピーを作成し、変更したアイテムを再構築してから、変更した状態の新しい配列(dbとdatasourceの両方)を保存します。

onCollapse(rowID: number) {
    console.log("rowID", rowID);
    var newArray = this.state.db.slice();
    newArray[rowID] = {
        key: newArray[rowID].key,
        details: newArray[rowID].details,
        isCollapsed: newArray[rowID].isCollapsed == false ? true : false,
    };
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(newArray),
        db: newArray,
    });
}
18
Dev01

reactは、dataSourceの変更を検出し、リストを再レンダリングする必要がある場合に十分にスマートです。 listViewを更新する場合は、既存のオブジェクトのプロパティを更新する代わりに、新しいオブジェクトを作成します。コードは次のようになります。

let newArray = this._rows.slice();
newArray[rowID] = {
  ...this._rows[rowID],
  Selection: !this._rows[rowID].Selection,
};
this._rows = newArray;

let newDataSource = this.ds.cloneWithRows(newArray);
this.setState({
  dataSource: newDataSource
});

同様の詳細についてはこちらをご覧ください Githubの問題

3
Dariy Dzyndra