web-dev-qa-db-fra.com

Liste de mise à jour React-Native DataSource

J'ai une application iOS que je crée avec react-native. La classe Game contient un composant ListView. J'ai défini l'état dans le constructeur et inclus un dataSource. J'ai un tableau de données codé en dur pour le moment que je stocke dans une propriété d'état différente (this.state.ds). Ensuite, dans componentDidMount, j'utilise la méthode cloneWithRows pour cloner mon this.state.ds En tant que source de données pour la vue. C'est assez standard dans la mesure où ListViews va et fonctionne bien. Voici le code:

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

Le problème que je rencontre concerne la méthode pressRow. Lorsque l'utilisateur appuie sur la ligne, je souhaite que la sélection soit modifiée et que les modifications soient rendues sur le périphérique. Lors du débogage, j'ai remarqué que même si je modifie la propriété Selection de l'objet dans le tableau newDs, la même propriété est modifiée sur l'objet dans this.state.ds Et de la même manière. change l'objet dans this.state.dataSource._dataBlob.s1. Suite au débogage, j'ai constaté que, puisque ces autres tableaux avaient changé, l'objet DataSource de ListView ne reconnaissait pas le changement, car lorsque je définis l'état et que rowHasChanged est appelé, le tableau qu'il est en train de cloner correspond au tableau this.state.dataSource._dataBlob.s1 Et cela ne ressemble donc pas à un changement et ne rend pas de nouveau compte.

Des idées?

40
Brandon M

Essaye ça:

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

}

Cela devrait faire une copie du tableau, qui peut ensuite être modifié indépendamment du tableau d'origine dans this.state.ds.

18
eddyjs

Au cas où quelqu'un rencontrerait ce problème comme moi, voici la solution complète.

Fondamentalement, il semble exister un bogue avec le composant ListView et vous devez reconstruire chaque élément modifié dans la source de données pour que ListView le redessine.

Commencez par créer la source de données et une copie du tableau, puis enregistrez-les dans l'état. Dans mon cas, foods est le tableau.

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

Lorsque vous souhaitez modifier quelque chose dans la source de données, faites une copie du tableau que vous avez enregistré dans l'état, reconstruisez l'élément avec la modification, puis enregistrez le nouveau tableau avec la modification dans l'état (db et source de données).

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 est suffisamment intelligent pour détecter les changements dans DataSource et si la liste doit être rendue à nouveau. Si vous souhaitez mettre à jour listView, créez de nouveaux objets au lieu de mettre à jour les propriétés des objets existants. Le code ressemblerait à ceci:

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

Vous pouvez en savoir plus sur semblable numéro sur Github

3
Dariy Dzyndra