反応ネイティブで作成しているiOSアプリがあります。 GameクラスにはListViewコンポーネントが含まれています。コンストラクターで状態を設定し、dataSource
を含めます。現在、別の状態プロパティ(this.state.ds
)に保存するデータのハードコードされた配列があります。次に、componentDidMount
でcloneWithRows
メソッドを使用して、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オブジェクトは変更を認識しません。そのため、変更のようには見えず、再レンダリングされません。
何か案は?
これを試して:
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
の元の配列とは無関係に変更できます。
私のように誰かがこの問題に出くわした場合、ここに完全な解決策があります。
基本的に、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,
});
}
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の問題