web-dev-qa-db-fra.com

Pourquoi setState dans reactjs async au lieu de Sync?

Je viens de constater que, dans la réaction this.setState(), la fonction de tout composant est asynchrone ou est appelée après l'achèvement de la fonction dans laquelle elle a été appelée.

Maintenant, j'ai cherché et trouvé ce blog ( http://www.bennadel.com/blog/2893-setstate-state-mutation-operation-may-be-synchronous-in-reactjs.htm )

Ici, il a constaté que setState est asynchrone (appelée lorsque la pile est vide) ou sync (appelée dès qu’elle est appelée) en fonction du déclenchement du changement d’état.

Maintenant, ces deux choses sont difficiles à digérer 

  1. Dans le blog, la fonction setState est appelée à l'intérieur d'une fonction updateState, mais ce qui a déclenché la fonction updateState n'est pas quelque chose qu'une fonction appelée connaîtrait.
  2. Pourquoi voudraient-ils rendre setState asynchrone car JS est un langage à un seul thread et que setState n'est pas un appel WebAPI ou serveur, il doit donc être effectué uniquement sur le thread de JS. Est-ce qu'ils font cela pour que le re-rendu n'arrête pas tous les écouteurs d'événements, ou qu'il y ait un autre problème de conception.
83
Anup

Vous pouvez appeler une fonction après la mise à jour de la valeur d'état:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

De même, si vous avez plusieurs états à mettre à jour simultanément, regroupez-les tous dans la même setState:

Instead of:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});


just do this:

this.setState({
    foo: "one",
    bar: "two"
});
116
JoeTidee

1.) Les actions setState sont asynchrones et sont mises en lot pour des gains de performances. Ceci est expliqué dans la documentation de setState.

setState () ne mute pas immédiatement this.state mais crée une transition d'état en attente. Accéder à this.state après avoir appelé cette méthode peut potentiellement renvoyer la valeur existante . Il n'y a aucune garantie de fonctionnement synchrone des appels à setState et les appels peuvent être mis en lot pour des gains de performances.


2.) Pourquoi rendraient-ils async de setState car JS est un langage à un seul thread et que setState n’est pas un appel WebAPI ou serveur:

Cela est dû au fait que setState modifie l'état et provoque la restitution. Cela peut être une opération coûteuse et le rendre synchrone peut laisser le navigateur ne pas répondre .
Ainsi, les appels setState sont asynchrones ainsi que mis en lots pour une meilleure expérience et performance de l'interface utilisateur.

63
Sachin

Je sais que cette question est ancienne, mais elle a longtemps semé la confusion dans l'esprit de nombreux utilisateurs de Reajs, dont moi-même . Récemment, Dan Abramov (de l'équipe de Réaction) vient d'écrire une excellente explication la nature de setState est asynchrone:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setState est censé être asynchrone, et il y a quelques très bonnes raisons à cela dans l'explication reliée de Dan Abramov. Cela ne veut pas dire que toujours sera asynchrone - cela signifie principalement que vous ne pouvez pas simplement dépendre sur le fait que synchronous. ReactJS prend en compte de nombreuses variables dans le scénario dans lequel vous modifiez l'état, pour décider quand la variable state doit réellement être mise à jour et votre composant rendu.
Un exemple simple pour illustrer cela est que si vous appelez setState en réaction à une action de l'utilisateur, alors la state sera probablement mise à jour immédiatement (bien que, là encore, vous ne puissiez pas compter sur elle), Vous ne ressentez aucun retard, mais si vous appelez setState en réponse à une réponse à un appel ajax ou à un autre événement non déclenché par l'utilisateur, l'état peut être mis à jour avec un léger retard, car l'utilisateur ne sentira pas vraiment Ce délai améliore les performances en attendant de regrouper plusieurs mises à jour d'état et de restituer le DOM moins de fois.

11
gillyb

Bon article ici https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-set-State.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

ou passer le callback this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

6
zloctb

Imaginez incrémenter un compteur dans un composant:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Un gestionnaire de compte est associé aux composants parent et enfant. Ceci est fait à dessein pour que nous puissions exécuter setState () deux fois dans le même contexte de propagation d'événements de clic, mais à partir de deux gestionnaires différents. 

Comme nous l’imaginions, un simple clic sur le bouton déclencherait maintenant ces deux gestionnaires car l’événement bouillonnait de la cible au conteneur le plus à l’extérieur pendant la phase de bullage. 

Par conséquent, btnCountHandler () s’exécute d’abord, afin d’incrémenter le compte à 1, puis divCountHandler () s’exécute, d’incrémenter le compte à 2. 

Toutefois, le nombre n'augmente que jusqu'à 1, comme vous pouvez l'inspecter dans les outils de React Developer.

Cela prouve que réagissent 

  • met en file d'attente tous les appels setState

  • revient à cette file après l'exécution de la dernière méthode du contexte (divCountHandler dans ce cas)

  • fusionne toutes les mutations d'objet se produisant dans plusieurs appels setState dans le même contexte (tous les appels de méthode dans une même phase d'événement correspondent au même contexte, par exemple) en une seule syntaxe de mutation d'objet (la fusion est logique car c'est la raison pour laquelle nous pouvons mettre à jour les propriétés de l'état dans setState () en premier lieu)

  • et le passe dans un seul setState () pour empêcher le rendu en raison d'appels multiples setState () (il s'agit d'une description très primitive du traitement par lots). 

Code résultant géré par react:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Pour arrêter ce comportement, au lieu de transmettre des objets en tant qu'arguments à la méthode setState, des rappels sont passés.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

Une fois que la dernière méthode a terminé son exécution et lorsque react retourne pour traiter la file d'attente setState, elle appelle simplement le rappel pour chaque setState en file d'attente, en passant à l'état précédent du composant.

De cette façon, réagit garantit que le dernier rappel de la file d'attente sera mis à jour comme tous les autres correspondants précédents.

1
supi

Vous pouvez utiliser l’habillage suivant pour faire un appel de synchronisation

this.setState((state =>{
  return{
    something
  }
})

0
Ярослав

Oui, setState () est asynchrone.

À partir du lien: https://reactjs.org/docs/react-component.html#setstate

  • React ne garantit pas que les changements d'état sont appliqués immédiatement.
  • setState () ne met pas toujours le composant à jour immédiatement.
  • Pensez à setState () comme une requête plutôt que comme une commande immédiate pour mettre à jour le composant.

Parce qu'ils pensent
À partir du lien: https://github.com/facebook/react/issues/11527#issuecomment-36019971

... nous sommes d'accord sur le fait que la restitution de setState () de manière synchrone serait inefficace dans de nombreux cas

Asynchronous setState () rend la vie très difficile pour ceux qui débutent et même malheureusement expérimenté:
- problèmes de rendu inattendus: rendu retardé ou non rendu (basé sur la logique du programme)
- passer des paramètres est un gros problème
entre autres.

L'exemple ci-dessous a aidé:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

J'espère que cela pourra aider.

0