There are two useState
hooks used in a component. If we allow for the event loop to "reflow" before modifying the state, each hook causes a separate re-render.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function App() {
const [foo, setFoo] = useState("initial_foo");
const [bar, setBar] = useState("initial_bar");
const onAction = useCallback(async () => {
await sleep(0); // this is the culprit
setFoo("updated_foo");
setBar("updated_bar");
}, []);
console.log(`rendering foo:${foo} bar:${bar}`);
return (
<>
<button onClick={onAction}>run test</button>
<div>{"foo: " + foo}</div>
<div>{"bar: " + bar}</div>
</>
);
}
console output with await sleep(0)
:
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
If we remove await sleep(0);
, everything works as expected: setFoo
and setBar
cause a single re-render.
console output without await sleep(0)
:
rendering foo:initial_foo bar:initial_bar
rendering foo:updated_foo bar:updated_bar
The demo (in the demo console output is duplicated, because it has React strict mode on)
- Why is it happening?
- Is it a bug or a feature?
- How can we prevent intermediate re-renders of this kind?
Update:
Issue has been created in the React project repo