Home > front end >  Execution order of useEffect in combination with updating state
Execution order of useEffect in combination with updating state

Time:09-23

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:

  1. MyComponent mounts and MyComponent() executes and returns JSX.
  2. function a is executed and the state change gets queued.
  3. function b is executed (The state change from 2) is not yet reflected, so state === "").

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:

  1. 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.

  2. 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:

  1. Your component will be initialized with a state value representing an empty string.

  2. The component will be mounted on the DOM.

  3. First useEffect runs, setting the value of the state to "Some value".

  4. Second useEffect runs (at this point, what this useEffect "sees" as the state's value is an empty string. Remember, React sees everything at a "render-level").

  5. Now, React will perform the scheduled component re-render, since you modified the state's value in the first useEffect.

  6. The component is re-rendered and now it "sees" the new state's value.

  7. 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.

  • Related