web-dev-qa-db-fra.com

Réaction de setState non mis à jour immédiatement

Je travaille sur une application à faire. Ceci est une version très simplifiée du code incriminé. J'ai une case à cocher:

 <p><input type="checkbox"  name="area" checked={this.state.Pencil}   onChange={this.checkPencil}/> Writing Item </p>

Voici la fonction qui appelle la case à cocher:

checkPencil(){
   this.setState({
      pencil:!this.state.pencil,
  }); 
  this.props.updateItem(this.state);
}

updateItem est une fonction mappée pour l'envoi à redux

function mapDispatchToProps(dispatch){
  return bindActionCreators({ updateItem}, dispatch);
}

Mon problème est que lorsque j'appelle l'action updateItem et console.log l'état, il y a toujours 1 étape de retard. Si la case à cocher est décochée et pas à true, l'état de true est toujours transmis à la fonction updateItem. Dois-je appeler une autre fonction pour forcer l'état à se mettre à jour? 

40
lost9123193

Vous devez appeler votre deuxième fonction en tant que rappel à setState, car setState se produit de manière asynchrone. Quelque chose comme:

this.setState({pencil:!this.state.pencil}, myFunction)

Cependant, dans votre cas, puisque vous voulez que cette fonction soit appelée avec un paramètre, vous devrez être un peu plus créatif et peut-être créer votre propre fonction qui appelle la fonction dans les accessoires:

myFunction = () => {
  this.props.updateItem(this.state)
}

Combinez ceux-ci ensemble et cela devrait fonctionner.

67
Ben Hare

L'appel de setState() dans React est asynchrone, pour différentes raisons (principalement les performances). Sous les couvertures, React regroupera plusieurs appels à setState() dans une mutation à un seul état, puis restituera le composant une seule fois, plutôt que de le restituer à chaque changement d'état.

Heureusement, la solution est assez simple - setState accepte un paramètre de rappel:

checkPencil(){
   this.setState({
      pencil:!this.state.pencil,
  }, function () {
      this.props.updateItem(this.state);
  }.bind(this));
}
13
rossipedia

Lorsque vous mettez à jour votre état à l'aide d'une propriété de l'état actuel, la documentation de React vous conseille d'utiliser la version d'appel de fonction de setState à la place de l'objet.

Donc setState((state, props) => {...}) au lieu de setState(object).

La raison en est que setState est davantage une demande de changement d'état que de changement immédiat. Les lots de réaction setState appellent une amélioration des performances. 

Cela signifie que la propriété d'état que vous vérifiez peut ne pas être stable . C'est un piège potentiel à connaître.

Pour plus d'informations, voir la documentation ici: https://facebook.github.io/react/docs/react-component.html#setstate


Pour répondre à votre question, je le ferais. 

checkPencil(){
    this.setState((prevState) => {
        return {
            pencil: !prevState.pencil
        };
    }, () => {
        this.props.updateItem(this.state)
    });
}
9
GBL

C'est parce que cela se produit de manière asynchrone , cela signifie que pendant ce temps, la mise à jour risque de ne pas être encore mise à jour ...

Selon la documentation de React v.16, vous devez utiliser une deuxième forme de setState() qui accepte une fonction plutôt qu'un objet:

Les mises à jour d'état peuvent être asynchrones

React peut regrouper plusieurs appels setState () dans une seule mise à jour pour performance.

Parce que this.props et this.state peuvent être mis à jour de manière asynchrone, vous ne devrait pas compter sur leurs valeurs pour calculer le prochain état.

Par exemple, ce code peut échouer pour mettre à jour le compteur:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

Pour résoudre ce problème, utilisez une deuxième forme de setState () qui accepte une fonction plutôt qu'un objet. Cette fonction recevra l'état précédent comme premier argument, et les accessoires au moment de l’application de la mise à jour comme second argument:

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));
8
Alireza

Ben a une excellente réponse sur la façon de résoudre le problème immédiat. Toutefois, je vous conseillerais également d'éviter toute duplication 

Si un état est en redux, votre case à cocher devrait lire son propre état à partir d'un accessoire ou d'un magasin au lieu de garder une trace de l'état de contrôle à la fois dans son propre composant et dans le magasin global.

Faites quelque chose comme ça:

<p>
    <input
        type="checkbox"
        name="area" checked={this.props.isChecked}
        onChange={this.props.onChange}
    />
    Writing Item
</p>

La règle générale est que si vous trouvez un état nécessaire à plusieurs endroits, placez-le devant un parent commun (pas toujours redux) pour ne conserver qu'une seule source de vérité.

2
CheapSteaks

essaye ça 

this.setState({inputvalue: e.target.value}, function () {
    console.log(this.state.inputvalue);
    this.showInputError(inputs[0].name);
}); 

fonction showInputError pour la validation si vous utilisez des formulaires

2
Anish

J'ai utilisé les suggestions de rossipedia et de Ben Hare et j'ai procédé comme suit: 

checkPencil(){
   this.setState({
      pencil:!this.state.pencil,
   }, this.updatingItem); 
}

updatingItem(){
    this.props.updateItem(this.state)
}
2
lost9123193

Commencez par définir votre valeur. après procéder vos travaux.

this.setState({inputvalue: e.target.value}, function () {
    this._handleSubmit();
}); 

_handleSubmit() {
   console.log(this.state.inputvalue);
   //Do your action
}
1
Kumaresan