Quelle est la méthode préférée pour muter un état React?

97

Disons que j'ai une liste d'objets simples dans mon this.state.listque je peux ensuite utiliser pour rendre une liste d'enfants. Quelle est alors la bonne façon d'insérer un objet dans this.state.list?

Voici la seule façon dont je pense que cela fonctionnera car vous ne pouvez pas muter this.statedirectement le comme mentionné dans la doc.

this._list.push(newObject):
this.setState({list: this._list});

Cela me semble moche. Y a-t-il un meilleur moyen?

Khoi
la source
duplication possible de la modification correcte des tableaux d'états dans ReactJS , demandant la fusion
Brigand
Toutes ces réponses concernent l'ajout d'un élément à un tableau. Mais qu'en est-il de la suppression d'un élément? Ou simplement pour réinitialiser complètement le tableau à un nouveau?
Augustin Riedinger

Réponses:

165

concat renvoie un nouveau tableau, donc vous pouvez faire

this.setState({list: this.state.list.concat([newObject])});

une autre alternative est l' assistant d'immuabilité de React

  var newState = React.addons.update(this.state, {
      list : {
        $push : [newObject]
      }
  });

  this.setState(newState);
Tas
la source
6
concatprend un tableau, donc vous voudrez this.state.list.concat([newObject]).
Sophie Alpert
@BenAlpert fonctionne pour moi dans Chrome, dites-vous qu'il s'agit d'un comportement non standard?
Heap
6
@Heap Eh bien, si vous lui passez un non-tableau, concatil l'enveloppera dans un tableau, mais il est préférable d'ajouter le tableau vous-même pour être explicite: si vous avez [[1,2], [3,4]]et voulez ajouter [5,6]comme élément suivant, vous devez faire .concat([[5,6]])autre chose va finir avec [[1,2], [3,4], 5, 6].
Sophie Alpert
2
Autant j'apprécie l'idée derrière les aides à l'immuabilité (et je peux finir par l'utiliser de toute façon), autant il Array.concatvaut mieux ajouter une autre bibliothèque.
Shawn Erquhart le
1
L'appel de this.setState avec une valeur dérivée de this.state vous fera tomber sous le coup des problèmes de mise à jour par lots. Voir stackoverflow.com/a/41445812/1998186 qui renvoie à un jsfiddle montrant le problème que vous rencontrez.
NealeU
40

setState () peut être appelé avec une fonction comme paramètre:

this.setState((state) => ({ list: state.list.concat(newObj) }))

ou dans ES5:

this.setState(function(state) {
  return {
   list: state.list.concat(newObj)
  }
})
Nyalab
la source
2
@Arian: React exécutera les instructions de mutation DOM requises pour rendre uniquement les éléments nouvellement ajoutés. Étant donné que vous êtes des éléments nouvellement ajoutés, cela ne change pas le rendu des éléments précédents.
Jose Browne
1
C'est une réponse fantastique et se sent tellement plus propre que le tableau concatant juste pour effectuer une poussée, même si c'est l'opinion que je suppose.
Matt Styles
2
@Nyalab ne devriez-vous pas mettre le corps de la fonction entre parenthèses? Dans l'état actuel de votre exemple, les accolades après la grosse flèche commenceraient un bloc, pas un objet. Quelque chose comme ça:this.setState((state) => ({ list: state.list.push(newObj) }))
kumarharsh
3
Comment fonctionne ce code? la méthode push retournera a NUMBER, qui sera défini sur la listvariable
kumarharsh
1
Vous pouvez également utiliser un splat:this.setState((state) => ({ list: [...state.list, newObj] }))
amoebe
19

Mise à jour 2016

Avec ES6, vous pouvez utiliser:

this.setState({ list: [...this.state.list, ...newObject] });
Ashish Chaudhary
la source
5
...[newObject]est le même que juste newObject.
amoebe
2
Cela ne fonctionnera pas pour les mises à jour par lots. Voir mon exemple sur stackoverflow.com/a/41445812/1998186 .
NealeU
7

À partir des documents de réaction ( https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous ):

Étant donné que this.props et this.state peuvent être mis à jour de manière asynchrone, vous ne devez pas vous fier à leurs valeurs pour calculer l'état suivant.

Vous devriez donc faire ceci à la place:

this.setState((prevState) => ({
  contacts: prevState.contacts.concat([contact])
}));
jcgzzc
la source