childrentime/reactuse

`useLocalStorage` causes `Maximum update depth exceeded` error if serializing/deserializing objects and using an object default

Closed this issue · 3 comments

When using useLocalStorage, values appear to be safely handled with normal use (as strings with a string default), but cause React render cycle problems if serialization options are used and an object is passed in for the default value.

For example, this works:

const useCompletedForms = () => {
  const [formsRaw, setFormsRaw] = useLocalStorage('completedForms', '[]');
  const completedForms = useMemo(() => JSON.parse(formsRaw ?? '[]'), [formsRaw]);
  const setCompletedForms = useCallback((forms: InputFormV2CompletedForms[]) => {
    setFormsRaw(JSON.stringify(forms));
  }, [setFormsRaw]);
  ...
}

However, this does not:

const completedFormsLocalStorageOpts = { serializer: { read: (raw: string): InputFormV2CompletedForms[] => raw ? JSON.parse(raw) : [], write: (x: InputFormV2CompletedForms[]) => JSON.stringify(x) } };

const useCompletedForms = () => {
  const [completedForms, setCompletedForms] = useLocalStorage('completedForms', [], completedFormsLocalStorageOpts);
  ...
}

In order to fix the above code, I have to use null instead of [] for the defaultValue parameter

This isn't a huge deal, as there's a pretty easy workaround. But at the very least, it would be helpful to note in the documentation until it's fixed, since the TypeScript typing heavily pushes you to use an array if your serializer serializes to an array

Thanks! I will remove defaultValue in effect deps.

@bfaulk96 I released a new version, take a try🥰

It's fixed, thank you!