astoilkov / use-local-storage-state

React hook that persists data in localStorage
MIT License
1.1k stars 41 forks source link

State in local storage not restored after page reload with SSR #39

Closed Bradzer closed 2 years ago

Bradzer commented 2 years ago

I'm using Next.js with SSR.

After saving my form data in local storage and refreshing the page, my form renders with the default values and not the values saved in local storage, although I have the option ssr enabled.

Here's the relevant part of my code:

export default function createClient() {
  const [
    formInitialValues,
    setFormInitialValues,
    { isPersistent, removeItem },
  ] = useLocalStorageState("awaji", {
    ssr: true,
    defaultValue: {
      lastName: "",
      firstName: "",
      gender: "male",
      dateOfBirth: "",
      initialCredit: 10000,
    },
  });

  const onSubmit = useCallback((values, actions) => {
    console.log(values);
    setFormInitialValues(values);
  }, []);

// rest of the code...

Is there something I'm doing wrong ?

astoilkov commented 2 years ago

I don't see anything suspicious in the code snippet.

I'm wondering if it can be caused by the submit that reloads the page before localStorage is able to store the data. Do you think that's possible?

Bradzer commented 2 years ago

@astoilkov I tried to check if it was really the reason why.

const myHandleSubmit = async (values, actions, setFormInitialValues) => {
  console.log(values);
  await setFormInitialValues(values);
  console.log("STORED IN LOCAL STORAGE");
};

export default function createClient() {
  const [
    formInitialValues,
    setFormInitialValues,
    { isPersistent, removeItem },
  ] = useLocalStorageState("awaji", {
    ssr: true,
    defaultValue: {
      id: "",
      lastName: "",
      firstName: "",
      gender: "male",
      dateOfBirth: "",
      address: "",
      phone: "",
      initialCredit: 10000,
    },
  });

  const onSubmit = (values, actions) => {
    myHandleSubmit(values, actions, setFormInitialValues);
    console.log("SUBMITTED");
  };

  useEffect(() => console.log("RENDERED"));

// rest of the code...

Here is the order of logs I got after submitting the form:

// 1st log
{
    address: "123 Main St"
    dateOfBirth: "2022-02-01"
    firstName: "b"
    gender: "male"
    id: "123456"
    initialCredit: "11000"
    lastName: "a"
    phone: "1234567890"
    __proto__: Object
}

// 2nd log
SUBMITTED

// 3rd log
STORED IN LOCAL STORAGE

// 4th log
RENDERED

So it seems like the data is well stored in localStorage as it should before the render. Yet the form renders the default data once I refresh the page.

It got me thinking that maybe Formik (which is the library I'm using for my forms) was involved and somehow was not using or ignoring the data in localStorage. So I browsed the documentation in hope to find something related to that behavior... and I did.

It looks like by default Formik does not reset the form if the data object has the same reference. By setting the Formik option enableReinitialize to true, Formik resets the form if the data changes using deep equality.(https://formik.org/docs/api/formik#enablereinitialize-boolean)

After enabling that option and refreshing the page, the form correctly uses the data stored in localStorage as expected.

astoilkov commented 2 years ago

Aha. Yes, that makes sense. Because ssr option first renders the default and then updates. Otherwise, you will get a hydration mismatch error.

Do you think there is something to do about this or should I close this issue?

Bradzer commented 2 years ago

@astoilkov you can close the issue. As far as I'm concerned I got it working so it's fine for me. Thank you ;)

astoilkov commented 2 years ago

I've made a change in the new use-local-storage-state release (version 16) that can possibly fix your issue without the need for enableReinitialize.

If you are interested, you can upgrade and try it out. If you do, please let me know the result as I'm very curious.