简体   繁体   中英

Dependencies in React useEffect cleanup function are not updated

I am facing a strange issue when trying to call a cleanup function on component unmount with useEffect.

In my useEffect return I call a useCallback function where the dependencies are added correctly. In there a check the state variable called status but this variable never get updated from the initial state. I cannot pass the variable to the useEffect as I want to trigger it only when the component unmounts for specific reasons. I recreated a simplified version in the codepen here and I can't get my head around this. Maybe someone knows why this is happening? Thank you! (this just started happening recently and it was working previously so I'm even more confused!)

Thank you for your answers. So, I finally found out what happens. useEffect creates a closure and the function is in that closure, which means that the status, being a string, remains as for the first render (when the closure gets created) and it never gets updated. A way of giving this is using useRef, as mentioned by @ilkerkaran, but that's because it creates an object, which means that the ref.current property has a link to the original one and it's always in sync. Another way would be to do useMemo and return an object with the status property, which is practically useRef under the hood. So practically, if the state were an object and we passed state as a dependency, the stayus property would work as expected for the same reason. I hope this helps also someone else and saves some time

Actually that's not what happens on your code. The callback function is updated according to dependency array. You can see that by calling remove() just above useEffect. That way, the func will be executed on every render.

What happens in your example;

  • it renders, (by pressing the toggle button for the first time)
  • then you trigger second render by calling setStatus("mounted") in useEffect (by pressing the toggle button for the second time)
  • then it renders for te last time for unmount with the default state values

Last part also bugs me actually. You can observe this behaviour by putting a simple console.log just above your useEffect definition.

You also can work around this by using useRef instead of useState

The reason for this is your useEffect .

 React.useEffect(() => {
    setStatus("mounted")
    return () => remove()
  }, []) 

You have an useEffect with the dependency set to []. This means that your useEffect will run only once. So this is how the flow goes your component is executed from the top to bottom so you create a remove function which at this point of time has your initial state as not mounted . Now your dom get painted. You useEffect gets called you set the state now you get a brand new remove function. Now you unmount your component the clean up will use the remove function from the first render .

In order for your state to reflect in the remove you need to add status as the dependency in the useEffect .

 React.useEffect(() => {
    setStatus("mounted")
    return () => remove()
  }, [status]) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM