React component state wiped before component unmounted
Asked Answered
B

2

5

If I return a function from useEffect I can be sure that that function will run when a component unmounts. But React seems to wipe the local state before it calls my unmounting function.

Consider this:

function Component () {

  const [setting, setSetting] = useState(false)

  useEffect(() => {

    setSetting(true)

    // This should be called when unmounting component
    return () => {

      console.log('Send setting to server before component is unmounted')
      console.log(setting) // false (expecting setting to be true)
      
    }
  }, [])
  
  return (
    <p>Setting is: {setting ? 'true' : 'false'}</p>
  )
}

Can anyone confirm that the expected behaviour is that the components state should be wiped? And, if that is the correct behaviour, how does one go about firing off the current component state to a server just before the component is unmounted?

To give some context, I'm debouncing a post request to a server in order to avoid firing it every time the user changes a setting. The debouncing works nicely, but I need a way to fire the request once a user navigates away from the page, as the queued debouncing method will no longer fire from the unmounted component.

Biebel answered 14/12, 2020 at 13:54 Comment(0)
W
7

It's not that React "wipes out the state value", it's that you have closure on setting value (the value on-mount).

To get expected behavior you should use a ref and another useEffect to keep it up to date.

function Component() {
  const [setting, setSetting] = useState(false);

  const settingRef = useRef(setting);

  // Keep the value up to date
  // Use a ref to sync the value with component's life time
  useEffect(() => {
    settingRef.current = setting;
  }, [setting])
  

  // Execute a callback on unmount.
  // No closure on state value.
  useEffect(() => {
    const setting = settingRef.current;
    return () => {
      console.log(setting);
    };
  }, []);

  return <p>Setting is: {setting ? "true" : "false"}</p>;
}
Weese answered 14/12, 2020 at 14:15 Comment(4)
"value is clearly being reset before the React component is unmounted" - That's not true. DO you have a reproducible example? Here is a code for you to play with: codesandbox.io/s/…Weese
You're right, the useEffect is obviously only running once and not running on changes to setting so is effectively a closure. It's just a shame that the solution of using a ref is so un-elegant. Thanks for your contribution.Biebel
I dont know what elegant solution means, but in React thats how you make it work. If by elegant you mean short, just put it in a custom hookWeese
There is certainly an elegance to working code. So, thank you. :-)Biebel
B
0

I have faced same issue, resolved by below code,

<TextField
            required
            id="title"
            name="title"
            label="Course Title"
            fullWidth
            autoComplete="given-name"
            variant="standard"
            inputRef={titleRef}
            value={title}
            onChange={(event) => setTitle(event.target.value)}
          />



  const [title, setTitle] = React.useState(courseDetails.title);

  const titleRef = React.useRef(title);

  React.useEffect(() => {
    const titleData = titleRef.current;
    console.log(titleRef.current + "@@@@@");
    return () => {
      console.log(titleData.value + "****");
    };
  }, []);
Bathilda answered 30/6, 2023 at 22:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.