Реагирующий список обновляемых данных DataSource
У меня есть приложение iOS, которое я делаю с реакцией. Класс Game содержит компонент ListView. Я устанавливаю состояние в конструкторе и включаю dataSource
. У меня есть жестко заданный массив данных прямо сейчас, когда я храню другое свойство состояния (this.state.ds
). Затем в componentDidMount
я использую метод cloneWithRows
для клонирования моего this.state.ds
в качестве моего источника данных для представления. Это довольно стандартно, поскольку 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)}>
</ListView>
);
}
}
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
. После дальнейшей отладки я обнаружил, что, поскольку эти другие массивы были изменены, объект ListView DataSource не распознает изменение, потому что, когда вызывается состояние и rowHasChanged
вызывается, массив, который он клонирует, соответствует массиву this.state.dataSource._dataBlob.s1
и так что это не похоже на изменение и не ререйдер.
Любые идеи?
Ответы
Ответ 1
Попробуйте следующее:
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
.
Ответ 2
В случае, если кто-нибудь столкнется с этой проблемой, как я, вот полное решение.
В принципе, кажется, что есть ошибка с компонентом ListView, и вам нужно перестроить каждый элемент, который изменяется в источнике данных, чтобы ListView перерисовал его.
Вот рабочий пример:
https://rnplay.org/apps/GWoFWg
Сначала создайте источник данных и копию массива и сохраните их. В моем случае 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,
});
}
Ответ 3
реакция достаточно умна, чтобы обнаруживать изменения в источнике данных и если список должен быть повторно отображен. Если вы хотите обновить 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