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.
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>
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.
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."
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
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).
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;
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.
La réponse courte à votre question est - NON, la réaction n’a pas de méthode de synchronisation setState
.