react-hook-form: getValues is empty or with default values on component unmount

Hey! Thanks a lot for this amazing lib. That’s what i was waiting for since hooks were released 😃 I’ve easily migrated forms from redux-form and formik to react-hook-form but faced one issue. So I’ve spotted a bug or maybe that’s how it should work. I have few forms which have like 5-6 fields in common. You could type in one form, accidentally close, or decided to switch to other form - all values you’ve entered were saved and new form was initialized with that values. Before I’ve got in parent component which renders all these forms a useState to save form values

const [savedValues, saveValues] = React.useState({})

Forms are rendered in modals and when I was closing the modal I was calling saveValues in return function of useEffect. Next time opening the form, values from savedValues were spreaded into initialValues of form. In react-hook-form I’ve found out that when you do like this

React.useEffect(() => () => {
  console.log(getValues());
}. []);

it is returning an empy object or default values if set.

Here is a reproducible example

Expected behavior getValues should return the last values

Additional context I’ve checked the sources and it seems that unmount and cleaning order should be different https://github.com/react-hook-form/react-hook-form/blob/master/src/useForm.ts#L948 https://github.com/react-hook-form/react-hook-form/blob/master/src/useForm.ts#L647 https://github.com/react-hook-form/react-hook-form/blob/master/src/useForm.ts#L465

Found out an adhoc solution - watch needed fields and save everything in ref. When unmount - get values from ref. Not very optimized because it will rerender form on every keystroke of watched fields

https://codesandbox.io/s/react-hook-form-basic-48z70?fontsize=14

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 18 (7 by maintainers)

Most upvoted comments

My apologies for the confusion 😃 forgot to add closure in useEffect in my first post. Updated.

Unfortunately that’s not an option for me because I don’t have by designs an option for user to save form values. But I found a better solution rather than using watch and it doesn’t feel like an Adhoc, I guess 😃

So the idea is to put unmount useEffect before useForm. Because in hooks order matters, the first useEffect would run before react-hook-form would run his own unmount useEffect cleaning the values. But we need to save reference to the getValues.

That’s how it looks

const getValuesRef = React.useRef(null);

React.useEffect(() => () => {
  if (getValuesRef.current) {
    console.log(getValues.current());
  }
}, []);

const { register, handleSubmit, getValues } = useForm();

// Save getValues reference on each rerender
React.useEffect(() => {
  ref.current = getValues;
});

Here is a working codesandbox

I think it’ll be good if we can add this to examples or FAQ on website because it’s a valid and widely used usecase so other people can easily find it.

Oh god I was dumbfounded since a normal effects works at development, but I got the exact Issue when in Production.

Thanks @Grimones , I got it working, though I wrote a small hooks to avoid duplication of codes.

Hereis the hook, usePersist

const usePostPersist = onFormUnMounting => {
  const getValuesRef = useRef(null)
  // Save RHF data when form is unmounted
  // Used to persist data
  useEffect(
    () => () => {
      const values = getValuesRef?.current && getValuesRef?.current()
      if (onFormUnMounting && values) {
        onFormUnMounting(values)
      }
    },
    []
  )
  const setGetValuesRef = getValues => {
    getValuesRef.current = getValues
  }
  return setGetValuesRef
}

Then I can just used it on any page that I need it like this

  const setGetValuesRef = usePersist(onFormUnMounting)
  const {
    register,
    handleSubmit,
    errors,
    control,
    reset,
    getValues,
  } = useForm({
    mode: 'onSubmit',
    resolver: yupResolver(mySchema),
    defaultValues: {},
  })
  useEffect(() => {
    setGetValuesRef(getValues)
  })

Then I can reuse this logic, without putting many codes that do similar.

Thanks a lot again

yes @allicanseenow any API from react-hook-form