Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue: useWatch / watch has wrong values when using form array with dnd kit #11909

Open
1 task done
alex-collibra opened this issue May 20, 2024 · 4 comments
Open
1 task done
Labels
question Further information is requested

Comments

@alex-collibra
Copy link

alex-collibra commented May 20, 2024

Version Number

7.43.9

Codesandbox/Expo snack

Codesandbox reproduction

Steps to reproduce

  1. Open provided codesandbox
  2. See there are 2 field there: one has conditional field for grade
  3. Drag the line with two fields to swap it with another one.
    There are two issues happening:
  4. useWatch in GradeField returns undefined value for additionalInformation and renders the component, even though type === 'adult' and so that conditional field should be hidden completely.
  5. useWatch in top lvl component shows unexpected values (grade property is now existing on both form rows, while it should only be existing for one row. The values in onSubmit are correct)

It seems like either useWatch runs earlier than expected or maybe there is a problem with keys (due to how drag overlay works)

Given that dnd kit react is widely used and they recommend using DragOverlay, it could be really helpful to have some examples / recommendations.

Thanks in advance 🙏

Expected behaviour

useWatch should show proper values.
useWatch should not return undefined for field which shouldn't even be rendered.

What browsers are you seeing the problem on?

Chrome

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@alex-collibra alex-collibra changed the title issue: useWatch / watch has wrong values when using from array with react drag and drop issue: useWatch / watch has wrong values when using form array with react drag and drop May 21, 2024
@alex-collibra alex-collibra changed the title issue: useWatch / watch has wrong values when using form array with react drag and drop issue: useWatch / watch has wrong values when using form array with d&d kit May 21, 2024
@alex-collibra alex-collibra changed the title issue: useWatch / watch has wrong values when using form array with d&d kit issue: useWatch / watch has wrong values when using form array with dnd kit May 21, 2024
@bluebill1049
Copy link
Member

Pretty sure it's related to this:

useWatch's execution order matters, which means if you update a form value before the subscription is in place, then the value updated will be ignored.

https://react-hook-form.com/docs/usewatch

@bluebill1049 bluebill1049 added the question Further information is requested label Jun 1, 2024
@alex-collibra
Copy link
Author

alex-collibra commented Jun 3, 2024

I see. Using useFormValues hook as suggested in the docs and getting type from useFormValues return value instead of accessing it using field kind of works.
The problem is that old values still shows up for a moment, where they shouldn't.

Before:

const useFormValues = () => {
  const { getValues } = useFormContext()

  return {
    ...useWatch(), // subscribe to form value updates

    ...getValues(), // always merge with latest form values
  }
}

// FormRow component

const FormRow = ({ index, field, isFromDragOverlay }) => {

  return (
      ...
      {field.type === 'student' && (
        <GradeField index={index} isFromDragOverlay={isFromDragOverlay} />
      )}
      ...
  )
}

After:

const FormRow = ({ index }) => {
  const values = useFormValues()
  const type = values.people[index].type

  return (
    ...
    {type === 'student' && <GradeField index={index} />} // <--- no need to pass isFromDragOverlay now or disable watch if field is from drag overlay - it will just work now
    ...
    )
}

@bluebill1049
Copy link
Member

This PR may address this sort of issue #11522 as subscription can be established before react component.

@BrendanC23
Copy link

@bluebill1049 Can you explain how useFormValues() works? Is this accurate?

  • useWatch() will re-trigger at the hook level (instead of the useForm() root level like watch()), but it doesn't always have the most up-to-date value because the update might happen before the subscription
  • getValues() will have the up-to-date values

Why does useFormValues() return { ...useWatch(), ...getValues() }? Why not just call useWatch() to ensure that the function runs when the subscription changes, and then return getValues() to get the latest values?

Is there any issues with doing something like this instead?

export function useWatchFormValues<
    TFieldValues extends FieldValues = FieldValues,
    TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(form: UseFormReturn<TFieldValues>, name: TFieldName) {
    useWatch({ control: form.control, exact: true, name: name }); // subscribe to form value updates
    const values = form.getValues(name);

    return values;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants