I have this basic code which updates a state from a handler:
const wait = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
export default function App() {
async function handleClick() {
setData(1);
console.log("first click");
console.log(data);
await wait(1000);
setData(2);
console.log("second click");
console.log(data);
}
const [data, setData] = React.useState(0);
return (
<div>
{data}
<button onClick={() => handleClick(setData)}>Click Me</button>
</div>
);
}
I am trying to understanding the order of operations, could someone please verify or point me in the right direction of what is happening? I have researched around but haven't found conclusive sources on what I think is happening.
- we click the button, triggering the handler
- the handler runs
setData(1)
, enqueuing a task to the event loop console.log('first click')
runs- we log the state (data), which is still 0, as the setter has only been enqueued
- we run into the wait function, so we exit out to the synchronous code flow as we wait for the 1000ms
- the sync code finishes running, so we dequeue the next task, which is the setter function, the state is now set to 1, and the view re-renders and reflects that new state
- after 1 second has elapsed, we return to the code after wait function
setData(2)
enqueues another task- 'second click' is logged
- 0 is stil logged, as our local state has not changed
- the sync code finishes, we dequeue the setter, re-rendering the view, causing 2 to show up
Is this all correct? Have I misunderstood anything? Thanks for any help.
CodePudding user response:
Yes, you've got this down correctly, except possibly for the bit
runs
setData(1)
, enqueuing a task to the event loop
This may or may not involve the event loop. What happens in setData
is specific to react.js, and won't necessarily enqueue a task in the browser event loop. It certainly does "somehow" schedule the state update and re-rendering of the component - within react.
If I remember correctly, react.js does schedule the render caused by setData(1)
for the end of the native click handler, i.e. when the call to your onClick
handler returns to react code. And for setData(2)
, the rendering might actually happen synchronously within the setData
call, as react does not control the context. This is however subject to change (the devs are talking about batching multiple updates together asynchronously) and should be considered an implementation detail. If you're curious, just place a console.log
in the render function of your component, but do not write any code that relies on the order observed in this test!