2015-07-31 21 views
38

Possiedo un'applicazione iOS con nativo react. La classe Game contiene un componente ListView. Ho impostato lo stato nel costruttore e include dataSource. Ho una matrice di dati hardcoded per ora che memorizzo in una proprietà di stato diversa (this.state.ds). Quindi nel componentDidMount utilizzo il metodo cloneWithRows per clonare il mio this.state.ds come dataSource per la visualizzazione. Questo è abbastanza standard per quanto riguarda ListViews e funziona bene. Ecco il codice:Visualizzazione elenco aggiornamento nativo DataSource

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

Il problema che sto avendo entra nel metodo pressRow. Quando l'utente preme la riga, desidero che la selezione cambi e che apporti la modifica sul dispositivo. Attraverso alcune operazioni di debug, ho notato che anche se sto modificando la proprietà Selection dell'oggetto nell'array newDs, le stesse modifiche di proprietà sull'oggetto in this.state.ds e analogamente viene modificato l'oggetto in this.state.dataSource._dataBlob.s1. Attraverso ulteriori debug, ho scoperto che dal momento che quegli altri array sono stati modificati, l'oggetto DataSource di ListView non riconosce la modifica perché quando si imposta lo stato e viene chiamato rowHasChanged, la matrice che clonazione corrisponde all'array this.state.dataSource._dataBlob.s1 e quindi non lo fa t sembra un cambiamento e non ritorna.

Qualche idea?

risposta

15

Prova questo:

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

} 

Questo dovrebbe effettuare una copia della matrice, che può poi essere modificata indipendentemente dalla matrice originale in this.state.ds.

+0

Grazie mille! C'era un altro passo che dovevo fare per farlo funzionare. Per qualche ragione ho dovuto "cancellare" l'oggetto che stavo cercando di cambiare. Così ho reso 'newDs [0]' uguale a un nuovo oggetto e gli ho dato le proprietà dell'oggetto che stavo cercando di cambiare. Ovviamente ho cambiato la proprietà 'Selection' però. –

+1

Cambia 'this.state.ds.slice()' in 'this.state.ds.slice (0)' per prestazioni migliori. – TheNickyYo

+0

@TheNickyYo - perché è così? I documenti dichiarati indefiniti vengono trattati come 0 in ogni caso? –

18

Nel caso in cui qualcuno si imbatta in questo problema come ho fatto, ecco la soluzione completa.

Fondamentalmente, sembra esserci un bug con il componente ListView ed è necessario ricostruire ogni elemento che cambia nell'origine dati per il ListView per ridisegnarlo.

Ecco un esempio di lavoro: https://rnplay.org/apps/GWoFWg

In primo luogo, creare l'origine dati e una copia della matrice e salvarli a stato. Nel mio caso, foods è la matrice.

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

Quando si vuole cambiare qualcosa nell'origine dati, fare una copia della matrice si è salvato allo stato, di ricostruire l'oggetto con il cambiamento e quindi salvare il nuovo array con il cambio di nuovo alla condizione (sia db e origine dati).

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

Invece di ricostruire manualmente l'intero oggetto, dovresti essere in grado di fare: 'var copy = Object.assign ({}, obj);' per documentazione MDN qui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign –

+2

Oppure se preferisci la sintassi ES6: 'newRows [rowId] = { ... this. _sampleData [rowId], preferito: rowData.favorited? false: true } ' –

+0

Potrebbe questo.db invece di this.state.db essere migliore? – iou90

3

reagire è abbastanza intelligente per rilevare le modifiche in dataSource e se l'elenco deve essere rieseguito. Se si desidera aggiornare listView, creare nuovi oggetti invece di aggiornare le proprietà degli oggetti esistenti. Il codice dovrebbe essere simile a questa:

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

Si può leggere di più su simili issue on Github