Race condition between useFieldArray and reset, useFieldArray continues to use initial defaultValues after reset

See original GitHub issue

Describe the bug

My team has a collection of forms which utilise all the same data loading and submission APIs, but have slightly different form content and data transformation needs. To keep our code DRY we use 2 components to render a form:

  1. a top-level “FormContainer” which calls useForm and sets up all the data fetching & submission, plus some other concerns outside the form
  2. an inner “FormContent” which handles transformation of the form data and rendering of the fields.

So responsibilities are divided up like this:

  • <FormContainer /> handles
    • data fetch
    • useForm (initialised with empty defaultValues)
    • <FormProvider />
    • submission
    • renders Spinner or FormContent based on loading states
  • <FormContent />
    • is passed data as a prop
    • transforms data as needed
    • calls useEffect -> reset and useFieldArray
    • renders the fields.

The bug: after structuring the forms this way we found that the line items we show with useFieldArray were not updating from the original default value once the data loaded.

the workaround: it turns out that the order of useEffect->reset and useFieldArray matters when hook-form is used in this way due to how resetFieldArrayFunctionRef is initialised internally. Basically useFieldArray has to be initialised before the useEffect resets the data or else it will continue consuming the original default values.

To Reproduce

This sandbox contains both the bug and a fix (comment/uncomment the relevant lines) https://codesandbox.io/s/react-hook-form-race-condition-bug-ytly7

  // Uncomment this for bug
  useEffect(() => {
    if (data) {
      reset(data);
    }
  }, [data, reset]);

  const { fields, remove } = useFieldArray({
    control,
    name: "names.test"
  });

  // Uncomment this for fix
  // useEffect(() => {
  //   if (data) {
  //     reset(data);
  //   }
  // }, [data, reset]);

Other comments

While finding this bug was definitely developer error on my part, and the fix was fairly straight-forward, it was extremely non-obvious what was wrong, and I had to go through hook-form’s build and add logs before I realised that reset didn’t have a reference to useFieldArray’s reset method at the time of calling reset.

Ideally there would be a way to fix the race condition so that useFieldArray picks up recently reset values when its own effects run, as I can imagine there’s a useEffect internally which is getting queued by React in the order of calling and hook-form was not written with this condition in mind.

Worst case, perhaps a warning in the console or documentation would do, that reset can only be queued after all field arrays have been initialised?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:8 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
Mario-Eiscommented, Sep 6, 2022

@Moshyfawn Oh wow, I wish I could. The issues started with a really complicated form. Maybe I am able to isolate the issue better. But at the moment I am struggling a bit.

Basically new data is loaded async from the backend when a button is clicked. When the data is received, the effect for resetting the form is triggered. Now there is really much going on on the page, so its initially slow. And the data is randomly updated correctly or merged together with the old form state. What I already found out is, that resetting the form in useLayoutEffect is the only way to avoid this issue…

I think it has nothing to do with the strict mode. Because the issues are also there in production mode.

But the issue comes from within the forms state. The new data and the old somehow gets merged together. The new array entries replace the old ones. But if the new array is shorter than the old one, the old entries are still there in the next state.

1reaction
Nick-Lucascommented, Nov 8, 2020

Thanks for looking into this, I totally understand it’s difficult to fix.

Documentation change looks good, thanks for the hard work on this project! 👌

Read more comments on GitHub >

github_iconTop Results From Across the Web

When reset is called asynchronously on react-hook-form's ...
But the fix in 7.22.0 only makes all elements in the field array undefined . It does not clear out the field array,...
Read more >
React Hook Form Reset useFieldArray - CodeSandbox
A custom hook for working with Field Arrays (dynamic inputs). 0. 1.5 ...
Read more >
Effective forms: building dynamic array fields with useFieldArray
Add the “Clear” button to reset the state of the whole form. Well, this looks pretty challenging. Let's implement the new requirements. Modifying...
Read more >
useFieldArray - React Cool Form - Netlify
The default value of the field. Useful for dealing with the case of conditional fields. validation#. (value: any, values: FormValues) => any |...
Read more >
useFieldArray - Simple React forms validation
control object provided by useForm . It's optional if you are using FormContext. shouldUnregister, boolean. Whether Field Array will be unregistered after ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found