Files
gruperly/.agents/skills/react-hook-form/references/formcfg-values-prop.md
2026-09-04 16:49:24 -03:00

2.6 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use the values Prop to Keep a Form in Sync with Server Data HIGH replaces a useEffect+reset that overwrites edits whenever the query refetches formcfg, values, resetOptions, react-query, server-state

Use the values Prop to Keep a Form in Sync with Server Data

When the initial data arrives from a query, the reflex is useEffect(() => reset(data), [data]). That works until the query refetches — on window focus, on interval, after an unrelated mutation — and the effect fires again mid-edit, wiping whatever the user had typed.

useForm({ values }) is the built-in answer. RHF re-syncs the form when the values reference changes, and resetOptions: { keepDirtyValues: true } tells it to leave fields the user has touched alone while updating the ones they haven't. defaultValues still supplies the shape before the first response lands.

Incorrect (effect-driven reset — a background refetch discards in-progress edits):

function ProfileForm({ userId }: { userId: string }) {
  const { data: profile } = useQuery({ queryKey: ['profile', userId], queryFn: fetchProfile })
  const { register, reset, handleSubmit } = useForm<ProfileFormValues>({
    defaultValues: { displayName: '', bio: '' },
  })

  useEffect(() => {
    if (profile) reset(profile)  // Fires again on every refetch, mid-edit
  }, [profile, reset])

  return (
    <form onSubmit={handleSubmit(saveProfile)}>
      <input {...register('displayName')} />
    </form>
  )
}

Correct (declarative sync that preserves what the user has touched):

function ProfileForm({ userId }: { userId: string }) {
  const { data: profile } = useQuery({ queryKey: ['profile', userId], queryFn: fetchProfile })
  const { register, handleSubmit } = useForm<ProfileFormValues>({
    defaultValues: { displayName: '', bio: '' },
    values: profile,
    resetOptions: { keepDirtyValues: true },
  })

  return (
    <form onSubmit={handleSubmit(saveProfile)}>
      <input {...register('displayName')} />
    </form>
  )
}

Which initialiser to reach for:

  • defaultValues — the shape and the baseline; required regardless (see formcfg-default-values)
  • values — the record is fetched and may change while the form is open
  • async defaultValues — the record is fetched once and will not change under the form (see formcfg-async-default-values)

Drop keepDirtyValues only when a server change should win over the user's edit — a record another person may be editing concurrently, for instance.

Reference: useForm - values