For class components, this.setState
calls batch if inside event handlers. But what happens if state is updated outside the event handler and using useState
hook?
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>
}
Will it render aa - bb
right away? Or it will be aa - b
and then aa - bb
?
TL;DR – if the state changes are triggered asynchronously (e.g. wrapped in a promise), they will not be batched; if they are triggered directly, they will be batched.
I've set up a sandbox to try this out: 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);
I've made two buttons, one triggers the state changes wrapped in a promise like in your code example, the other triggers the state changes directly.
If you look at the console, when you hit the button “with promise”, it will first show a aa
and b b
, then a aa
and b bb
.
So the answer is no, in this case, it will not render aa - bb
right away, each state change triggers a new render, there is no batching.
However, when you click the button “without promise”, the console will show a aa
and b bb
right away.
So in this case, React does batch the state changes and does one render for both together.