Capítulo 21 de 39

Chapter 21: setValue

Core Idea

setValue(name, value, options?) dynamically updates a registered field's value while minimizing re-renders, with opt-in flags for whether the write should also trigger validation, mark dirty, or mark touched.

Key Concepts

  • value is required, never undefined.
  • shouldValidate: recomputes validity for that field (and isValid), not the whole form's errors.
  • shouldDirty: marks the target field dirty; because isDirty always compares live values against defaultValues, a later unrelated update can still recompute dirtyFields and surface other fields that already differ from default — even ones previously written with shouldDirty: false.
  • shouldTouch (7.8.0+): marks the field touched.
  • delayError (7.82.0+): opt-in per-call flag to use the delay configured via useForm({ delayError }); only applies when shouldValidate: true is also set.
  • Prefer the leaf field path over a nested object: setValue("yourDetails.firstName", "value") is more performant than setValue("yourDetails", { firstName: "value" }); also, setValue on a dot-path only finds a field if that exact path was registered — updating a field that was registered with a nested object value (register("nestedValue", { value: {...} })) must target the same nested shape, not a sub-path.
  • Field arrays: setValue can write directly into array entries (setValue("array.0.test1", "1")) without remounting, unlike useFieldArray's update/replace which do remount; for replacing an entire array, replace from useFieldArray is the purpose-built, preferred API — setValue on the whole array works today but may be discouraged later.
  • Works on unregistered fields, but registering first is preferred: calling setValue on a name nobody registered implicitly creates that field; for a nested shape, registering each leaf explicitly first lets a single setValue call on the parent update all of them correctly.

Code Examples

setValue("firstName", "Bill")
setValue("firstName", "Luo", { shouldValidate: true, shouldDirty: true })
  • What it demonstrates: a silent write vs. one that also revalidates and marks dirty.
const [a, b] = watch(["a", "b"])

useEffect(() => {
  if (formState.touchedFields.a && formState.touchedFields.b && a && b) {
    setValue("c", `${a} ${b}`)
  }
}, [setValue, a, b, formState])
  • What it demonstrates: deriving one field's value from two others once both have been touched — the standard "dependent field" pattern.

Anti-patterns

  • Setting a whole nested object when only a leaf path was registered: setValue won't find the field; target the exact registered path instead.
  • Using setValue to replace an entire field array instead of useFieldArray's replace: works today, but replace is the intended, more explicit tool and this usage may be discouraged later.
  • Assuming shouldDirty: false permanently protects a field from ever showing as dirty: a later update elsewhere can still recompute and surface it as dirty via the isDirty/defaultValues comparison.

Key Takeaways

  1. setValue is the imperative write path — pair it with shouldValidate/shouldDirty/shouldTouch deliberately, since none of them are on by default.
  2. Target the field's exact registered name, not a broader nested object, for both correctness and performance.
  3. For field-array replacement, prefer useFieldArray's replace/update over setValue.

Connects To

  • useform-watch / usewatch: commonly paired to derive one field's value from others.
  • usefieldarray: replace/update as the purpose-built alternative for array writes.
  • useform: the delayError option this method's per-call flag opts into.