React-hook-form: the watch method and values from useFieldArray are out of sync

Created on 4 Apr 2020  路  44Comments  路  Source: react-hook-form/react-hook-form

Describe the bug
The watch method does not recognize the updates from useFieldArray. In the codesandbox i am console logging all watched values out. Any changes from the form field values should be reflected in that watch method, including the modified values in useFieldArray but in this case the values are not accurate.

To Reproduce
Steps to reproduce the behavior:

  1. Go to the codesandbox
  2. open console
  3. Click append
  4. Then click delete twice
  5. See error Object {test: Array[3]} (it should be a length of 2)
  6. Click append again.
  7. See that the array index is one behind now.
  8. Delete all items and see there is still an array of 3.

Codesandbox link (Required)
https://codesandbox.io/s/issue-usefieldarray-watch-broken-kph9x

Expected behavior
When fields are modified from useFieldArray, the watch method should reflect the same modified array. This way we are able to get the subscribe to the values externally for our needs.

Desktop (please complete the following information):

  • OS: Mac
  • Browser: chrome

Additional context

The reason we need this to work is for using the watch method combined with useEffect to dispatch changes to a context provider so the user can see a _live update_ of their form. In our case, we don't want to manually setup onChange events for every field but watch/subscribe to all fields and update our context so we can display the live information elsewhere.

under investigation

Most helpful comment

Hi @bluebill1049!

Just wanted to chime in and make our use-case completely clear so you're not throwing darts in a dark room.

Our team is coming to the end of building a platform which has Campaigns which map to various web pages. We have a live-preview builder where the user can change settings for their Campaign and watch the affects live. To do this we are watching for values to change in the form and then are passing the values up to a Context which is used by the previewer. As such, it's important to us that we're able to know _when_ values change and dispatch the new values.

We're keeping track of our dependencies on the project and are budgeting that, post release, we'll be able to financially support our dependencies on a regular basis, as well as contribute ourselves. So we're hoping to partner with RHF long-term and have a great on-going relationship where we show our support and appreciation with our open-source partners.

If there's any more context we can provide to help clear up our use-case, please feel free to ask any questions.

All 44 comments

Try watch that array field : watch('test')

thanks for report this issue: https://codesandbox.io/s/issue-usefieldarray-watch-broken-m5x4h patch is coming.

@bluebill1049 Amazing turn around time! Did you happen to notice, though, that if you remove _all_ of the instances it snaps back to the initial values instead of an empty array?

Yea, that's expected behaviour for watch. I do see your concern tho. you may need to use formState like touched/dirty to counter with this issue.

Hmm... I can see how we'd check to see if the field is/dirty touched to know if it's been modified, but I'm not sure how that would help. If the field has been dirty but is back to its default state, we can't assume that means it's empty. Would you mind elaborating a bit more on your idea?

In my mind there's a fundamental difference between the initial state of a field and its watched state. I would think that watch only returns the default value until the field is dirty, at which point it returns whatever its actual state is. In what scenario would it make sense for a field to appear default in a dirty state?

yea @JasonTheAdams, i agree with your statement above. The reason behind such behaviour is so when user invoke watch the first time will not return undefined since none of the inputs have been registered.

Let me do some investigation, see if we can improve this behaviour, worse case i will add a description in the doc and provide alternative solution with formState.

Awesome! Thank you!

@bluebill1049 This issue still persists in v5.3.1

I'm not sure what happened because you had the issue fixed in your patch.

Here is the codesandbox:
https://codesandbox.io/s/issue-usefieldarray-watch-broken-v531-bwovf

  1. add append
  2. notice nothing gets added to the array
  3. click delete
  4. notice justWatchedValue is undefined & allWatchedValue is not right either
  5. append again and we're one value behind.

strangle... I will take a look at it.

Much appreciated! useFieldArray is our last roadblock to refactor fully into RHF from Final Form. Everything else is working great! 馃檶

I found the issue: reason behind

  1. release a version
  2. found out some of the automation were skipped (only)
  3. turn them back on, many things failed due to the commit
  4. revert the code change.

automation saved my a** last weekend... also this fix should be correct now. I may have to release a beta version for you guys to unblock you. I am bit hesitated to release stuff during the weekdays (due to my job if something goes wrong, i wouldn't be able to fix)

Hahah! The dark side of automation. 馃槅

I'd be fine with a beta since it's specifically for this issue (which we greatly appreciate). We can then immediately report back on this and hopefully bump up to the next stable release next week.

thanks for understanding @JasonTheAdams 馃檹

So just to make sure we're on the same page, @bluebill1049, we should expect to see a beta release some point soon?

I will release a beta once the build pass.

Oh and after my breakfast 馃ぃ

Perfect! 馃嵆 馃崬 馃

here you go: 5.3.2-beta.1

Much appreciated!

Thank you for helping us with this @bluebill1049 !

After testing out the beta version It seems we're 90% there but unfortunately some more issues arise. I'll explain what's happening and maybe you can help answer some questions.

updated sandbox: https://codesandbox.io/s/issue-usefieldarray-watch-broken-v532-beta1-bwovf?file=/src/App.js

1. remove() deletes array key from watched values before returning correct values

The original issue here is technically fixed (watch method and values form useFieldArray are in sync) except anytime you remove an item from the field array the watch value deletes the entire field array key from the watch values before returning the correct value. This causes some issues for us because we are syncing those watched values to our context and there will be a blink in render of those values because they disappear before appearing again.

  • click append
  • click remove
  • notice a missing array for field array key before returning accurate watched values

2. formState is not working properly

It seems something happened to formState dirtyFields and touched because they stay empty and false when interacting with the form.

3. Question about watch

When you watch all fields watch() does the amount of fields cause the amount re-renders?

4. Thoughts on a feature request

Something that final form has that works really nice is called Form Spy. We have been trying to mimick this behavior using this approach:

    const allWatchedValues = watch({nest: true}, initialValues);

    const allWatchedPreviousValues = usePrevious(allWatchedValues);

    const allWatchedValuesIsEqual = isEqual(allWatchedValues, allWatchedPreviousValues);

    useEffect(() => {
        if (onChange && allWatchedValues) {
            onChange(allWatchedValues);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [allWatchedValuesIsEqual]);

I'm wondering what you think about adding a way to do this in RHF. some things that may work are adding an onChange callback param to watch() or creating something new like subscription(values, onChange).

Interested in hearing your thoughts on this.

Thanks!

Thanks for the detailed report:

  1. remove() deletes array key from watched values before returning correct values

RHF is uncontrolled (normal form should be) even with useFieldArray, because of that If you are watching the entire Field Array, you basically treat it as controlled. so we will need to send an extra re-render to update e the watch returned value (only applied to Field Array, because field gets to add and remove), this is expected.

  1. formState is not working properly

I will need to see a codesandbox, if there is bug around, we will fix it.

  1. Question about watch()

I would not recommend watch(), i think i have stated that in the doc. it's basically trigger re-render on each key stroke like controlled.

  1. Thoughts on a feature request

I will read into it, thanks :)

Thanks for the reply!

  1. okay I understand, but i'm confused on why the entire array key disappears when removing an item from the array because this doesn't happen when you append. also those values are stored in default values so re-render shouldn't affect the array key at all. It seems like there is an issue with the remove() method.

  2. https://codesandbox.io/s/issue-usefieldarray-watch-broken-v532-beta1-bwovf?file=/src/App.js

  3. Yes, that's we want it for. We are building many live preview forms so while the user is typing they will see the value being live updated elsewhere. So, we are using watch() to listen for all form input onChange events to send those values to our react context wrapper.

  4. thanks!

Let's take a look 1, 2 tmr.

Hi @bluebill1049!

Just wanted to chime in and make our use-case completely clear so you're not throwing darts in a dark room.

Our team is coming to the end of building a platform which has Campaigns which map to various web pages. We have a live-preview builder where the user can change settings for their Campaign and watch the affects live. To do this we are watching for values to change in the form and then are passing the values up to a Context which is used by the previewer. As such, it's important to us that we're able to know _when_ values change and dispatch the new values.

We're keeping track of our dependencies on the project and are budgeting that, post release, we'll be able to financially support our dependencies on a regular basis, as well as contribute ourselves. So we're hoping to partner with RHF long-term and have a great on-going relationship where we show our support and appreciation with our open-source partners.

If there's any more context we can provide to help clear up our use-case, please feel free to ask any questions.

Thanks @JasonTheAdams for the detailed explanation.

2: dirtyFields is working as expected
Screen Shot 2020-04-08 at 9 00 51 am

useFieldArray touched formState is not refreshing the form; looks like touched is working as well :
Screen Shot 2020-04-08 at 9 04 35 am

Issue 2 is working at my end, maybe i miss the step to reproduce. let me know how did you produce the issue.

Issue 1: needs more time to investigate, will spend sometimes today over the lunch break or tonight.

I can successfully reproduce issue 1: creating a seperate issue for this.

useFieldArray with remove action return empty array

codesandbox:
https://codesandbox.io/s/issue-usefieldarray-watch-broken-v532-beta1-bwovf?file=/src/App.js

steps:

  1. click on remove
  2. inspect console

Thanks for the fast response @bluebill1049 you rock!

Re: Issue 2. I was thinking formState touched & dirtyFields would change if you interact with the field array - maybe that's not the case?

No worries @jonwaldstein

Issues 2: i used your code example and it's working for me, take a look the screenshots as well.

@bluebill1049
Re: Issue 2

Okay I see that when you click or type into any items in the field array it will trigger touched/dirtyFields and add the array items individually to the dirtyFields set. Although formState.dirty will become true if you use any of the field array buttons without clicking or typing into the inputs (this is what I was expecting with dirtyFields too).

When a user clicks any of the fieldArray buttons like append and remove it should add the field array key to dirtyFields - that way we would know if that field array as a whole is dirty since it's also making the formState.dirty true. Since those dirtyFields are a set i wonder if you can just add the key to the set when its dirty and then continue to add the array items like this: ['test', 'test[1]', 'test[2]'] than you could search for your array key like formState.dirtyFields.has('test') when needed.

  • touched: i believe is corrected behaviour, this should only set to true when user blue the input
  • dirtyFields: I am not 100% sold on the idea.

changes the value, turning the control dirty

Do you have any good reference to back that behaviour dirtyFields?

RE: dirtyFields

Okay I don't want to get too off track with this because it's not the main issue and I know you're working hard on fixes, but I would like you to think about the relationship between formState.dirty and formState.dirtyFields with me.

If formState.dirty becomes true shouldn't formState.dirtyFields show you _what_ is dirty?

This relationship can be summed up by asking two questions (codesandbox below):

console.log("[is form state dirty?]", formState.dirty);
console.log("[if yes what fields are dirty?]", [...formState.dirtyFields]);

You see, when I click a button in field array the formState.dirty becomes true. Except dirtyFields never tells me what specifically is dirty.

https://codesandbox.io/s/issue-dirtyfields-v532-beta1-6w2y8?file=/src/App.js

RE: dirtyFields

Okay I don't want to get too off track with this because it's not the main issue and I know you're working hard on fixes, but I would like you to think about the relationship between formState.dirty and formState.dirtyFields with me.

If formState.dirty becomes true shouldn't formState.dirtyFields show you _what_ is dirty?

This relationship can be summed up by asking two questions (codesandbox below):

console.log("[is form state dirty?]", formState.dirty);
console.log("[if yes what fields are dirty?]", [...formState.dirtyFields]);

You see, when I click a button in field array the formState.dirty becomes true. Except dirtyFields never tells me what specifically is dirty.

https://codesandbox.io/s/issue-dirtyfields-v532-beta1-6w2y8?file=/src/App.js

That's a valid point for dirtyFields. 馃I think you are right here, will fix this behaviour.

Touched fields not getting updated when user append/prepend item

Okay I don't want to get too off track with this because it's not the main issue and I know you're working hard on fixes, but I would like you to think about the relationship between formState.dirty and formState.dirtyFields with me.

If formState.dirty becomes true shouldn't formState.dirtyFields show you what is dirty?

This relationship can be summed up by asking two questions (codesandbox below):

console.log("[is form state dirty?]", formState.dirty);
console.log("[if yes what fields are dirty?]", [...formState.dirtyFields]);
You see, when I click a button in field array the formState.dirty becomes true. Except dirtyFields never tells me what specifically is dirty.

https://codesandbox.io/s/issue-dirtyfields-v532-beta1-6w2y8?file=/src/App.js

closing this issue, all issues have been distributed into seperate issue tickets to track and fix.

Thanks @bluebill1049 馃檶

@bluebill1049 unfortunately have to re-open this issue along with something going on with reset():

Please visit the codesandbox below:

https://codesandbox.io/s/issue-rhf-values-v541-f7ye2?file=/src/App.js

  1. Open console
  2. click delete
  3. notice the field array key is missing (console logging twice) before showing correct value
    Screen Shot 2020-04-13 at 4 48 05 PM
  4. click submit (the button is being conditionally shows when form is dirty)
  5. notice error

In v5.3.1 we were able to use reset(values) on submit to reset the form state with the new values. As you can see from this example, there appears to be a problem with the functionality of using reset(values). Please let me know if you need anymore details. Thanks!

looks like someone else reported this issue as well.

let's track it there: https://github.com/react-hook-form/react-hook-form/issues/1404 cause it's more specific.

Thanks a lot for the detailed issue report as well.

Was this page helpful?
0 / 5 - 0 ratings