web-dev-qa-db-fra.com

React Native - Forcer le rendu de ListView lorsque les données n'ont pas changé

Est-il possible de forcer un ListView à effectuer un nouveau rendu, même si les données de la source de données n'ont pas changé? J'ai un ListView dans une barre d'onglets de mon application et je souhaite le redessiner à chaque fois que cet onglet est sélectionné, que les données soient identiques ou aient été modifiées.

this.state = {
  data: props.data,
  dataSource: new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
}

componentWillMount() {
  this.setState({
    dataSource: this.state.dataSource.cloneWithRows(nextProps.data)
  })
}

render() {
  <ListView
   dataSource={this.state.data}
   renderRow={this._renderRow}
  />
}

J'ai essayé de jouer avec les arguments rowHasChanged mais cela n'a pas aidé. Toute aide serait très appréciée

9
Brien Crean

Donc, si j'ai bien compris, votre cas d'utilisation consiste à rendre à nouveau toutes les lignes de ListView en raison des modifications apportées à value. Je ne sais pas ce qu'est value, à vous de le déterminer, mais je vais utiliser value pour expliquer ma réponse:

Il y a plusieurs façons de résoudre ce problème. chacun a ses avantages et ses inconvénients:

  • moyen facile: seulement <ListView key={value} ... /> !! Il indique à React que ListView doit être rendu de nouveau lorsque la valeur change (il sera démonté, remonté).
  • "façon hacky". remplacer par un nouveau DataSource. this.setState({ dataSource: new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }).cloneWithData(this.props.data) });
  • la meilleure pratique (imo): dans les données, incorporez value et implémente correctement rowHasChanged. Par exemple: (r1, r2) => r1.value !== r2.value && r1.data !== r2.data (<- C’EST JUSTE un moyen possible de représenter les données, vous devez choisir votre format, je suppose que vous avez cloné le source de données avec nextProps.data.map(data => ({ data, value })).

pour moi, la troisième et dernière solution est la meilleure parce que:

  • il s'adapte bien à plus de condition de re-rendu (autre values)
  • il peut arriver que seule une partie de vos lignes doive être restituée et, dans ce cas, vous ne devriez pas refaire le rendu de toutes les lignes pour de meilleures performances.

voici une autre réponse que j'ai donnée à ce sujet:

Quelles sont les entrées exactes de rowHasChanged dans ListView.DataSource

32
gre

Utiliser une approche d'immuabilité

Le meilleur moyen de "mettre à jour" cela serait de cloner l'élément de données en question plutôt que de procéder à un hack d'indicateur, car le r1 !== r2 l'indique déjà. Au lieu de mettre à jour un élément individuel, modifiez le "pointeur" lui-même par clonage.

Pour ce problème particulier, mettez à jour l'élément dans le tableau comme suit.

arr[i] = { ...arr[i], someKey: newValue }

Cela remplacera la propriété "someKey" par "newValue" et la ligne actuelle aura un nouveau pointeur.

Voici une bonne ressource pour en savoir plus sur l'immuabilité dans JavaScript moderne qui réagit aux utilisations natives: https://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/

2
Jason Sebring

C’est probablement un peu exagéré, mais je remplace simplement la source de données sur composantDidUpdate (c’est peut-être mauvais pour les performances? Je ne sais pas, haha)

  componentWillMount() {
   this.ds = new ListView.DataSource({
     rowHasChanged: (r1, r2) => r1 !== r2,
    });
   this.dataSource = this.ds.cloneWithRows(this.props.shiftsList);
  }

componentDidUpdate() {
  this.ds = new ListView.DataSource({
   rowHasChanged: (r1, r2) => r1 !== r2,
  });
 this.dataSource = this.ds.cloneWithRows(this.props.shiftsList);
}
0
Sam Matthews

Je sais que je suis un peu en retard mais je suis sûr que cela reste d'actualité. Il existe une approche encore plus simple à cet égard ... Le premier exemple fourni par Jason est certainement une bonne solution, mais je pense à ceux qui s'embrouillent facilement. Si oui, utilisez ceci.

// On component initalization
componentWillMount() {
    this.UpdateData();
}

// When the component updates
componentDidUpdate() {
    if (this.state.data != this.props.data)
        this.UpdateData();
}

// Function to update the ListView component data
UpdateData() {
    var source = new ListView.DataSource({
        rowHasChanged: (r1, r2) => r1 !== r2
    });

    this.setState({
        data: this.props.data,
        dataSource: source.cloneWithRows(this.props.data)
    });
}

Je recommande également d'implémenter Redux dans votre application. Ainsi, vous pouvez stocker la nouvelle valeur de tableau mise à jour dans un réducteur, puis la stocker localement dans un état et utiliser ultérieurement une instruction if dans la fonction composantDidUpdate pour déterminer si les données sont mises à jour ou non.

Le débogueur de React n'enregistre aucune plainte ni aucun avertissement basé sur les performances ...

0
Engineer