Given the following simple component:
const MyComponent = () => {
const [state, setState] = useState("");
useEffect(function a() {
// Do stuff
setState("Some value");
}, [])
useEffect(function b() {
// Do stuff
}, [state]);
// return some fancy JSX
}
The execution order will be:
MyComponent
mounts andMyComponent()
executes and returns JSX.- function
a
is executed and the state change gets queued. - function
b
is executed (The state change from 2) is not yet reflected, sostate === ""
).
Now my question is, given that state
might change by other triggers than drafted here, is there any guarantee that the setState("Some value")
from function a
will have happened by the time the effect b
is executed the next time (after step 3) above)?
In other words, may there be any constellation where b
will be called because state
has changed, but the setState("Some value")
from a
has not happened yet?
CodePudding user response:
Yes it's very possible. Here's the demo:
function Home() {
const [state, setState] = useState("");
useEffect(function a() {
// Do stuff
setTimeout(() => {
setState("Some value");
}, 10000);
}, [])
useEffect(function b() {
// Do stuff
}, [state]);
// return some fancy JSX
return <>
<button onClick={() => setState('this is executed first')}>
hello
</button>
<h1>{state}</h1>
</>
}
The state will update as soon as you clicked the button and then changed again after 10 seconds (from function A)
CodePudding user response:
There are a few key things that we have to clearly understand:
In React, state behaves like some sort of "snapshot". Setting the state doesn't change the state variable you already have, but instead triggers a re-render.
The UI doesn't directly change in response to a user's event (like a click, for example). When you update a state, a new render is queued, and React will render the component according to the new state value in that render.
What I try to explain with these points is that React sees everything at a render-level, in other words, when React calls your component it gives you a "snapshot" of the state for that particular render. The functions, event handlers, etc. inside your component will all "see" the state's value for that render.
Based on this, what would happen in your example will be something like:
Your component will be initialized with a state value representing an empty string.
The component will be mounted on the DOM.
First
useEffect
runs, setting the value of the state to"Some value"
.Second
useEffect
runs (at this point, what thisuseEffect
"sees" as the state's value is an empty string. Remember, React sees everything at a "render-level").Now, React will perform the scheduled component re-render, since you modified the state's value in the first
useEffect
.The component is re-rendered and now it "sees" the new state's value.
Your second
useEffect
will fire again because it notices that the state's value changed, comparing the previous value from the previous render (an empty string) to this new value in this new render.
Short answer: what you describe is indeed possible but very unlikely to happen if you don't do overly weird stuff and you also structure well enough your code.
I hope this is clear enough.