Pour les composants de classe, this.setState
appelle batch si à l'intérieur des gestionnaires d'événements. Mais que se passe-t-il si l'état est mis à jour en dehors du gestionnaire d'événements et en utilisant le crochet useState
?
function Component() {
const [a, setA] = useState('a');
const [b, setB] = useState('b');
function handleClick() {
Promise.resolve().then(() => {
setA('aa');
setB('bb');
});
}
return <button onClick={handleClick}>{a}-{b}</button>
}
Sera-t-il rendu aa - bb
tout de suite? Ou ce sera aa - b
et alors aa - bb
?
TL; DR - si les changements d'état sont déclenchés de manière asynchrone (par exemple enveloppés dans une promesse), ils ne seront pas groupés; s'ils sont déclenchés directement, ils seront groupés.
J'ai mis en place un sandbox pour essayer ceci: https://codesandbox.io/s/402pn5l989
import React, { Fragment, useState } from 'react';
import ReactDOM from 'react-dom';
import './styles.css';
function Component() {
const [a, setA] = useState('a');
const [b, setB] = useState('b');
console.log('a', a);
console.log('b', b);
function handleClickWithPromise() {
Promise.resolve().then(() => {
setA('aa');
setB('bb');
});
}
function handleClickWithoutPromise() {
setA('aa');
setB('bb');
}
return (
<Fragment>
<button onClick={handleClickWithPromise}>
{a}-{b} with promise
</button>
<button onClick={handleClickWithoutPromise}>
{a}-{b} without promise
</button>
</Fragment>
);
}
function App() {
return <Component />;
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
J'ai fait deux boutons, l'un déclenche les changements d'état enveloppés dans une promesse comme dans votre exemple de code, l'autre déclenche directement les changements d'état.
Si vous regardez la console, lorsque vous appuyez sur le bouton "avec promesse", elle affichera d'abord a aa
et b b
, puis a aa
et b bb
.
Donc, la réponse est non, dans ce cas, cela ne rendra pas aa - bb
tout de suite, chaque changement d'état déclenche un nouveau rendu, il n'y a pas de batch.
Cependant, lorsque vous cliquez sur le bouton "sans promesse", la console affiche a aa
et b bb
tout de suite.
Donc, dans ce cas, React effectue par lots les changements d'état et effectue un rendu pour les deux ensemble.
Actuellement dans React v16 et versions antérieures, seules les mises à jour à l'intérieur de React les gestionnaires d'événements tels que click
ou onChange
etc sont groupées par Par conséquent, tout comme les mises à jour d'état des classes sont groupées de la même manière dans les crochets
Il existe une API instable pour forcer le traitement par lots en dehors des gestionnaires d'événements pour les cas rares lorsque vous en avez besoin.
ReactDOM.unstable_batchedUpdates(() => { ... })
Il est prévu de regrouper toutes les mises à jour d'état dans la future version sur React probablement v17 ou supérieur.
Maintenant aussi, si les appels de mise à jour d'état depuis le gestionnaire d'événements sont dans des fonctions asynchrones ou déclenchés en raison du code asynchrone, ils ne seront pas groupés là où les mises à jour directes seront groupées
Où sans le code de synchronisation, les mises à jour sont groupées et les mises à jour de code asynchrone ne le sont pas
function App() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// async update from useEffect
useEffect(() => {
setTimeout(() => {
setCount1(count => count + 1);
setCount2(count => count + 2);
}, 3000);
}, []);
const handleAsyncUpdate = async () => {
await Promise.resolve("state updated");
setCount1(count => count + 2);
setCount2(count => count + 1);
};
const handleSyncUpdate = () => {
setCount1(count => count + 2);
setCount2(count => count + 1);
};
console.log("render", count1, count2);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button type="button" onClick={handleAsyncUpdate}>
Click for async update
</button>
<button type="button" onClick={handleSyncUpdate}>
Click for sync update
</button>
</div>
);
}