React-Native Updating List View DataSource

Tengo una aplicación de iOS que estoy haciendo con react-native. La class Game contiene un componente ListView. Configuré el estado en el constructor e dataSource un dataSource . Tengo una matriz de datos codificados para este momento que this.state.ds en una propiedad de estado diferente ( this.state.ds ). Luego, en el componentDidMount utilizo el método cloneWithRows para clonar my this.state.ds como mi dataSource para la vista. Eso es bastante estándar en lo que respecta a ListViews y funciona bien. Aquí está el código:

 /** * 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 

El problema que estoy teniendo viene en el método pressRow . Cuando el usuario presiona la fila, me gustaría que la selección cambiara y que representara el cambio en el dispositivo. A través de algunas depuraciones, he notado que aunque estoy cambiando la propiedad Selection del object en la matriz newDs , la misma propiedad cambia en el object en this.state.ds y, de manera similar, cambia el object en this.state.dataSource._dataBlob.s1 . A través de una debugging adicional, he descubierto que, dado que esas otras matrices han cambiado, el object DataSource de ListView no reconoce el cambio porque cuando establezco el estado y se rowHasChanged , la matriz que está clonando coincide con el array this.state.dataSource._dataBlob.s1 y por lo tanto no parece un cambio y no rerender.

¿Algunas ideas?

Prueba esto:

 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) }) } 

Esto debería hacer una copy de la matriz, que luego se puede modificar independientemente de la matriz original en this.state.ds .

En caso de que alguien se encuentre con este problema como lo hice, aquí está la solución completa.

Básicamente, parece que hay un error con el componente ListView y necesita rebuild cada elemento que cambia en el origen de datos para ListView para volver a dibujarlo.

Aquí hay un ejemplo de trabajo: https://rnplay.org/apps/GWoFWg

Primero, cree el origen de datos y una copy de la matriz y guárdelos en estado. En mi caso, los foods son la matriz.

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

Cuando desee cambiar algo en la fuente de datos, haga una copy de la matriz que guardó en el estado, reconstruya el elemento con el cambio y luego guarde la nueva matriz con el cambio de nuevo en el estado (tanto en db como en fuente de datos).

 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, }); } 

reaccion es lo suficientemente inteligente como para detectar cambios en dataSource y si la list debe volverse a representar. Si desea actualizar listView, cree nuevos objects en lugar de actualizar las properties de los objects existentes. El código se vería así:

 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 }); 

Puede leer más sobre un problema similar en Github