web-dev-qa-db-fra.com

Y at-il une alternative synchrone de setState () dans Reactjs

Selon l'explication dans la docs :

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.

Donc, puisque setState() est asynchrone et qu’il n’existe aucune garantie quant à ses performances synchrones. Existe-t-il une alternative de setState() qui soit synchrone.

Par exemple 

//initial value of cnt:0
this.setState({cnt:this.state.cnt+1})
alert(this.state.cnt);    //alert value:0

Puisque la valeur alert est la valeur précédente, quelle est l'alternative qui donnera alert value:1 en utilisant setState().

Il y a peu de questions sur Stackoverflow qui sont similaires à cette question, mais pas où je suis capable de trouver la bonne réponse.

24
shubham agrawal

Comme vous l'avez lu dans la documentation, il n'y a pas d'alternative sync, la raison décrite étant les gains de performances.

Cependant, je suppose que vous souhaitez effectuer une action après avoir modifié votre état, vous pouvez y parvenir via:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     x: 1
    };
    
    console.log('initial state', this.state);
  }
  
  updateState = () => {
   console.log('changing state');
    this.setState({
      x: 2
    },() => { console.log('new state', this.state); })
  }
  
  render() {
    return (
      <div>
      <button onClick={this.updateState}>Change state</button>
    </div>
    );
   
  }
}

ReactDOM.render(
  <MyComponent />,
  document.getElementById("react")
);
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

43
ospfranco

Non, il n'y en a pas. React mettra à jour l'état lorsqu'il le jugera utile, en effectuant des opérations telles que le traitement en lot des appels setState pour plus d'efficacité. Cela peut vous intéresser que vous puissiez passer une fonction dans setState, qui prend l’état précédent, afin que vous puissiez choisir votre nouvel état avec une bonne connaissance du précédent. 

2
Jemar Jones

Si cela est nécessaire, je suggérerais d'utiliser un rappel dans votre fonction setState (et également d'utiliser un setState fonctionnel).

Le rappel sera appelé après la mise à jour de l'état.

Par exemple, votre exemple serait 

//initial value of cnt:0
this.setState(
    (state) => ({cnt: state.cnt+1}),
    () => { alert(this.state.cnt)}
)

selon la documentation ici: https://facebook.github.io/react/docs/react-component.html#setstate

Remarque: Les documents officiels indiquent: "En règle générale, nous vous recommandons d'utiliser le composantDidUpdate () pour une telle logique."

1
S.Kiers

Vous pouvez insérer setState dans une fonction renvoyant une promesse, puis utiliser cette fonction avec le mot-clé wait pour que votre code attende que l'état ait été appliqué.

class MyComponent extends React.Component {

    function setStateSynchronous(stateUpdate) {
        return new Promise(resolve => {
            this.setState(stateUpdate, () => resolve());
        });
    }

    async function foo() {
        // state.count has value of 0
        await setStateSynchronous(state => ({count: state.count+1}));
        // execution will only resume here once state has been applied
        console.log(this.state.count);  // output will be 1
    }
} 

Dans la fonction foo, le mot clé await provoque l'exécution du code en pause jusqu'à ce que la promesse renvoyée par setStateSynchronous soit résolue, ce qui ne se produit qu'une fois le rappel transmis à setState appelé, ce qui ne se produit que lorsque l'état a été appliqué. Ainsi, l'exécution n'atteint l'appel console.log qu'une fois la mise à jour d'état appliquée.

docs pour async/wait:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

1
ModestLeech

J'ai été capable de piéger React en appelant setState de manière synchrone en encapsulant mon code dans setTimeout(() => {......this.setState({ ... });....}, 0);. Étant donné que setTimeout place des éléments à la fin de la file d'attente d'événements JavaScript, je pense que React détecte que setState s'y trouve et sait qu'il ne peut pas compter sur un appel setState traité par lots (qui serait ajouté à la fin de la file d'attente).

0
Dustin Kane

Cela peut sembler étrange, mais oui, setState peut fonctionner de manière synchrone dans reag. Comment? C'est POC que j'ai créé pour le démontrer.

Coller le seul code JS de l'application.

Peut-être est-il possible que je manque quelque chose, mais cela se passait réellement dans ma demande et c'est alors que j'ai appris à connaître cet effet.

Corrigez-moi si ce genre de comportement est attendu dans React dont je ne suis pas au courant. Quand il y a plusieurs setState sur le thread principal, setState exécute un lot combinant tout le setState de la méthode main. Alors que le scénario est différent lorsque les mêmes choses entrent dans la fonction asynchrone.

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 0
    }
    this.asyncMethod = this.asyncMethod.bind(this);
    this.syncMethod = this.syncMethod.bind(this);
  }

  asyncMethod() {
    console.log("*************************")
    console.log("This is a async Method ..!!")
    this.setState({
      counter: this.state.counter + 1
    }, () => {
      console.log("This is a async Method callback of setState. value of counter is---", this.state.counter);
    })
    console.log("This is a async Method on main thread. value of counter is---", this.state.counter);
    console.log("*************************")
  }

  syncMethod() {
    var that = this;
    console.log("*************************")
    console.log("This is a sync Method ..!!")
    that.setState({counter: "This value will never be seen or printed and render will not be called"});
    that.setState({counter: "This is the value which will be seen in render and render will be called"});
    setTimeout(() => {
      that.setState({counter: "This is part is synchronous. Inside the async function after this render will be called"});
      console.log("setTimeout setState");
      that.setState({counter: "This is part is aslso synchronous. Inside the async function after this render will be called"});
    }, 10)
    console.log("This is a sync Method on Main thread. value of counter is---", this.state.counter);
    console.log("*************************")
  }

  render() {
    console.log("Render..!!",this.state.counter);
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
        </header>
          <button onClick={this.asyncMethod}>AsyncMethod</button>
          <button onClick={this.syncMethod}>SyncMethod</button>
      </div>
    );
  }
}

export default App;
0
Hussain Saify

Oui, il existe une méthode avec laquelle nous pouvons créer notre setState synchrone. Mais ses performances ne sont peut-être pas aussi bonnes que normalement Par exemple, nous avons 

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
     data: 0
    };
  }

  changeState(){
   console.log('in change state',this.state.data);
   this.state.data = 'new value here'
   this.setState({});
   console.log('in change state after state change',this.state.data);
  }

  render() {
    return (
      <div>
      <p>{this.state.data}</p>
      <a onClick={this.changeState}>Change state</a>
    </div>
    );

  }
}  

Dans cet exemple, nous modifions d'abord l'état, puis rendons notre composant.

0
Pulkit Aggarwal

La réponse courte à votre question est - NON, la réaction n’a pas de méthode de synchronisation setState.

0
Vladimir Kovpak