web-dev-qa-db-fra.com

React - Changer l'état sans utiliser setState: faut-il l'éviter?

Mon code fonctionne, mais j'ai une question de bonne pratique: j'ai un tableau d'objets dans l'état et une interaction utilisateur va changer la valeur d'un objet à la fois. Autant que je sache, je ne suis pas censé changer d'état directement, je devrais toujours utiliser setState à la place. Si je veux éviter cela à n'importe quel prix, je vais profondément cloner le tableau par itération et changer le clone. Puis définissez l'état sur le clone. A mon avis, éviter de changer l'état que je changerai plus tard ne fait que diminuer ma performance.

Version détaillée: this.state.data est un tableau d'objets. Il représente une liste de sujets dans un forum et un bouton Favoris basculera, appelant clickCollect(). Étant donné que j'ai un tableau dans l'état, lorsque je modifie la propriété is_collected d'un élément, je dois créer une copie du tableau avec lequel travailler, puis, après avoir modifié la nouvelle valeur, je peux le définir sur l'état.

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

var data = this.state.data: Ceci copierait le pointeur sur le tableau et Push (), shift (), etc. modifieraient directement l'état. data et this.state.data seront affectés.

var data = this.state.data.slice(0): Cela crée un clone superficiel. Push and shift ne change pas l'état, mais dans mon clone, j'ai toujours des pointeurs sur les éléments du tableau de l'état. Donc, si je change de data[0].is_collected, this.state.data[0].is_collected sera également modifié. Cela se produit avant que j'appelle setState().

Normalement je devrais faire:

var data = []; 
for (var i in this.state.data) {
    data.Push(this.state.data[i]); 
}

Ensuite, je modifie la valeur à l'index en lui attribuant la valeur true lorsqu'il est faux ou faux lorsqu'il est vrai:

data[index].is_collected = !data[index].is_collected;

Et changer d'état:

this.setState({data: data});

Considérer que mon tableau est relativement grand ou énormément grand, je suppose que cette itération va réduire les performances de mon APP. Je paierais ce coût si je savais que c'est la bonne façon pour une raison quelconque. Cependant, dans cette fonction (clickCollect), je mets toujours la nouvelle valeur à l'état, je n'attends pas une fausse réponse d'API qui indiquerait d'arrêter de faire le changement. Dans tous les cas, la nouvelle valeur entrera dans l'état. Dans la pratique, je n’appelle setState que pour que l’interface rende à nouveau. Donc les questions sont:

  1. Dois-je créer le clone profond dans ce cas? (for var i in ...)
  2. Sinon, est-il judicieux de faire un clone peu profond (.slice(0)) si mon tableau contient des objets? Les modifications sont en cours sur les objets à l'intérieur du tableau, de sorte que le clone superficiel change encore mon état, comme le ferait une copie (data = this.state.data).

Mon code est simplifié et les appels d'API sont conçus pour la simplicité.

C'est une question de débutant, une approche totalement différente est donc la bienvenue. Ou des liens vers d'autres questions et réponses.

import React from 'react';

var ForumList = React.createClass({
  render: function() {
      return <div className="section-inner">
        {this.state.data.map(this.eachBox)}
      </div>
  },
  eachBox: function(box, i) {
    return <div key={i} className="box-door">
        <div className={"favorite " + (box.is_collected ? "on" : "off")} onTouchStart={this.clickCollect.bind(null, i)}>
          {box.id}
        </div>
    </div>
  },
  getInitialState: function() {
    return {data: [
      {
        id: 47,
        is_collected: false
      },
      {
        id: 23,
        is_collected: false
      },
      {
        id: 5,
        is_collected: true
      }
    ]};
  },
  clickCollect: function(index) {
    var data = this.state.data.slice(0);
    data[index].is_collected = !data[index].is_collected;
    this.setState({data: data});
  }
});

module.exports = ForumList;
8
Szalai Laci

Personnellement, je ne suis pas toujours la règle, si vous comprenez vraiment ce que vous essayez de faire, alors je ne pense pas que ce soit un problème. 

var data = this.state.data.slice(0);
data[index].is_collected = !data[index].is_collected;
this.setState({data: data});

Dans ce cas, il est correct de changer d'état et d'appeler la variable setState comme ceci

this.state.data[index].is_collected = !this.state.data[index].is_collected;
this.setState({data: this.state.data});

La raison pour laquelle vous devriez éviter de modifier votre état est que si vous avez une référence à this.state.data et appelez setState plusieurs fois, vous risquez de perdre vos données:

const myData = this.state.data
myData[0] = 'foo'
this.setState({ data: myData })
// do something...
// ...
const someNewData = someFunc()
this.setState({ data: someNewData })

myData[1] = 'bar' // myData is still referencing to the old state
this.setState({ data: myData }) // you lose everything of `someNewData`

Si cela vous inquiète vraiment, optez pour immutable.js

3
CodinCat

Si vous voulez suivre les meilleures pratiques, vous devriez faire une copie superficielle de tout votre tableau, lorsque vous modifiez une propriété. Veuillez vous pencher sur l'implémentation "immuable" de la bibliothèque. 

Mais, de par mon expérience et selon moi, la méthode setState devrait être appelée si vous avez des implémentations "shouldCompomenentUpdate". Si vous pensez que votre copie superficielle consommera beaucoup plus de ressources, réagissez aux vérifications dom virtuelles, vous pouvez le faire:

this.state.data[0].property = !this.state.data[0].property;
this.forceUpdate();
1
degr

Le fait de mettre l'état en sourdine brise directement le principe fondamental du flux de données de React (conçu pour être unidirectionnel), ce qui rend votre application très fragile et ignore en gros le cycle de vie du composant.

Ainsi, bien que rien ne vous empêche réellement de muter l’état du composant sans setState ({}), vous devrez éviter cela à tout prix si vous voulez vraiment tirer parti de React, sinon vous sauteriez une des fonctionnalités de base de la bibliothèque.

0
Kelvin De Moya

Si j'ai bien compris votre question, vous avez un tableau d'objets et lorsqu'une propriété d'un seul objet dans un tableau change,

  1. Créez un clone profond du tableau et passez-le à setState 
  2. Créer un clone peu profond et passer à setState

Je viens de vérifier avec reduxsample todo app et si une seule propriété d'un objet change, vous devez créer une nouvelle copie de cet objet unique et non de l'ensemble du tableau. Je vous recommande de lire à propos de redux et, si possible, de gérer l'état de votre application.

0
VJAI