tiaanduplessis/react-hook-form-persist

useFormPersist causes the component to rerender on every field change

adams-family opened this issue · 3 comments

I'm not sure if this is fixable (but I hope it is). After setting up useFormPersist using the repro steps below, my page where useForm sits gets rerendered on every field change:

import useFormPersist from 'react-hook-form-persist'
const { setValue, watch } = form;

useFormPersist("form-storage", {
  watch, 
  setValue,
  storage: window.localStorage
});  

By commenting out the useFormPersist command above the problem is resolved but obviously the functionality is gone.

Motivation: Measured by Chrome DevTools / Performance tab, there is a 4-fold performance degradation with this command turned on (moving from 100ms to 400-500ms on my test case).

Suggestion: I think that if possible, the form should be persisted without causing the current element to rerender.

Me too

Yes, watch can be used with callback.

  React.useEffect(() => {
    const subscription = watch((value, { name, type }) =>
      console.log(value, name, type)
    )
    return () => subscription.unsubscribe()
  }, [watch])

https://react-hook-form.com/docs/useform/watch

Okay so I've run into this and was able to work through it by doing two things:

  1. I had change the internals of useFormPersist to use the newer useWatch hook that react-hook-form provides. This causes the interface of useFormPersist to change because now instead of passing in watch, you pass in control.
  2. The useFormPersist hook must be nested inside of a child component.

These two things fix the rendering issues because it causes that child component to re-render only instead of the main form component.

interface FormPersistProps {
  form: UseFormReturn<any>;
  formKey: string;
}

function FormPersist(props: FormPersistProps): ReactElement {
  const { form, formKey } = props;
  useFormPersist(formKey, {
    control: form.control,
    setValue: form.setValue,
    storage: isBrowser() ? window.sessionStorage : undefined,
  });
  return <></>;
}

Then I just have this nested like so:

function MainForm() {
  const form = useForm();
  return (
    <div>
      <FormPersist form={form} formKey={"mykey"} />
      <form>...</form>
    </div>
  );
}

This will not work with this current version of useFormPersist due to the old watch() method not working the same as the useWatch hook.
Can read more about it here: https://react-hook-form.com/docs/usewatch

PR where I made these changes if folks are interested: https://github.com/nucleuscloud/neosync/pull/1891/files