Skip to content

fix(setValues): update fields registered under an object or array value - #13667

Merged
bluebill1049 merged 1 commit into
react-hook-form:masterfrom
official-burak:fix/set-values-container-paths
Aug 20, 2026
Merged

bluebill1049 merged 1 commit into
react-hook-form:masterfrom
official-burak:fix/set-values-container-paths

Conversation

@official-burak

@official-burak official-burak commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Proposed Changes

setValues does not update a field that is registered at a path holding an object or an array. The input keeps its previous value even though getValues() reports the new one.

The batch loop flattens the incoming values and then matches mounted field names against the resulting keys:

const flattenedUpdates = flatten(updatedFormValues as FieldValues);

for (const fieldName of _names.mount) {
  if (fieldName in flattenedUpdates) {
    _setValue(fieldName, flattenedUpdates[fieldName], options, true, true);
  }
}

flatten only emits leaf paths, so { tags: ['b', 'c'] } becomes { 'tags.0': 'b', 'tags.1': 'c' }. A field registered as tags is never in that map, so _setValue is skipped and the element is left alone. An empty array or object is worse: it flattens to no keys at all, so the update disappears entirely.

In practice this hits the DOM controls whose value is an array under a single field name:

  • <select multiple {...register('tags')} /> keeps its old selection
  • a checkbox group sharing one name keeps its old checked state
  • a container path registered directly by a Controller never receives the new value
  • setValues({ tags: [] }) does not clear anything

setValue('tags', ['b', 'c']) handles all of these correctly today, so the two APIs disagree.

This replaces the flatten-and-lookup with a path walk over the provided values, so a container value is passed to _setValue as-is. _setValue already knows how to apply object and array values. It routes them through setFieldValue/setFieldValues, which handle multiple selects, checkbox groups and nested objects.

Path presence is resolved with own properties rather than in, which keeps two existing behaviours intact: an explicitly provided undefined still propagates to a mounted field, and a field name that collides with an inherited property (toString) is not treated as provided. The new has util mirrors get's path semantics, including a path stored as a single literal key such as { 'a.b': 1 }, so has and get agree on what is present.

flatten itself is unchanged and still used by jsonToFormData, where leaf-only output is what is wanted.

No linked issue. Found while reading the setValues batch path.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes

Tests

Three regression tests in setValues.test.tsx cover a multiple select, a checkbox group, and clearing a multiple select with []. All three fail on master:

● setValues › should update a multiple select registered under an array value
    - Expected  - 2
    + Received  + 1
      Array [
    -   "b",
    -   "c",
    +   "a",
      ]

● setValues › should update a checkbox group registered under an array value
    expect(element).not.toBeChecked()
    Received element is checked:
      <input aria-label="a" name="choices" type="checkbox" value="a" />

● setValues › should clear a multiple select when an empty array is provided
    Expected length: 0
    Received length: 1
    Received object: [<option value="a">a</option>]

Tests: 3 failed, 18 passed, 21 total

has also gets its own unit test alongside the other src/utils tests.

Full suite after the change:

pnpm test
Test Suites: 120 passed, 120 total
Tests:       1265 passed, 1265 total
Snapshots:   8 passed, 8 total

pnpm lint reports no new warnings, pnpm type and pnpm test:type are clean, and pnpm build succeeds. Bundle impact on dist/index.cjs.js is +45 B gzipped (13739 B to 13784 B), within the 14.0 kB bundlewatch budget.

`setValues` flattened the incoming values and matched mounted field names
against the resulting leaf paths. A field registered at a path that holds an
object or an array therefore never matched, because `flatten` only emits leaf
paths, so its input was left untouched. This affected multiple selects and
checkbox groups, whose value is an array under a single field name, and any
container path that a `Controller` registers directly. An empty array or object
was dropped altogether, since it flattens to no keys at all.

Match mounted field names against the values by walking the path instead, so a
container value reaches `_setValue`, which already knows how to apply it. Path
presence is checked with own properties so that an explicitly provided
`undefined` still propagates while an absent field stays untouched.

@bluebill1049 bluebill1049 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bluebill1049
bluebill1049 merged commit dead471 into react-hook-form:master Aug 20, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants