Using useEffect cleanup function to store form values in localStorage

Viewed 3093

I am using the useEffect cleanup function to store my formik values in localStorage. The strange part is the values stored are stale initial values and not the current form values. Here is the code:

  console.log(values)
  React.useEffect(() => {
    // do stuff
    return function() {
      console.log(values)
      // store form data on unmount
      localStorage.setItem("formValues", JSON.stringify(values))
    };
  }, []);

The values logged outside the useEffect function are current, the one logged (and stored) within the cleanup function are stale initial values. I though the function would be lazily evaluated so the values variable would have the current value. As a shot in the dark, I also tried using a getter function for the form values. But still the old stale values. What's happening here?

2 Answers

There are two ways to fix this problem.

  1. pass values inside the [] of useEffect.
    React.useEffect(() => {
    // do stuff
    return function() {
      console.log(values)
      // store form data on unmount
      localStorage.setItem("formValues", JSON.stringify(values))
    };
  }, [values]);

But in this way, it will save everytime values is changed So not appropriate if you only save once when the component is unmounted

  1. Second method (useRef)
    const formikRef = React.useRef();
    formikRef.current = useFormik({
      initialValues: ...,
      onSubmit: ...,
      validationSchema: ...
    });
    React.useEffect(() => {
      // do stuff
      return () => {
        console.log(formikRef.current.values);
        // store form data on unmount
        localStorage.setItem(
          "formValues",
          JSON.stringify(formikRef.current.values)
        );
      };
    }, []);

  const formik = formikRef.current;

If you want cleanup just on component unmount this is what you want:

const valuesRef = useRef(values);

useEffect(() => {
    valuesRef.current = values;
}, [values]);

useEffect(() => {
    // do stuff
    return function() {
      console.log(valuesRef.current)
      // store form data on unmount
      localStorage.setItem("formValues", JSON.stringify(valuesRef.current))
    };
}, []);

Beacuase you want just cleanup and update localstorage on component unmount, you should pass empty array ([]) to useEffect. If you pass an empty array ([]), the props and state inside the effect will always have their initial values (https://reactjs.org/docs/hooks-effect.html). It's because each effect “belongs” to a particular render that in this case it's the first render when component is mounted. But it might be better to update localstorage on each values change and in this case you only need to pass [values] to useEffect.

Related