Skip to content

fix(useController): reflect cleared parent object in controlled fields (#13550) - #13553

Merged
bluebill1049 merged 4 commits into
react-hook-form:masterfrom
EduardF1:fix/controller-clear-parent-object
Jun 30, 2026
Merged

fix(useController): reflect cleared parent object in controlled fields (#13550)#13553
bluebill1049 merged 4 commits into
react-hook-form:masterfrom
EduardF1:fix/controller-clear-parent-object

Conversation

@EduardF1

Copy link
Copy Markdown
Contributor

Fixes #13550

Problem

When a parent object field is replaced wholesale via setValue (for example setting data to null when there are controlled fields on data.type), the controlled fields kept their stale value and went out of sync with getValues() / useWatch.

Root cause

useController subscribes through useWatch with exact: true. In shouldSubscribeByName, exact matching only accepted strict name equality (currentName === signalName). setValue('data', null) emits a change with name: 'data', which never matches the child subscription data.type, so the controlled field was never notified and never re-rendered.

This is also why useWatch (which defaults to exact: false) reflected the cleared value while useController did not — exactly the mismatch reported in the issue.

Fix

In exact mode, also notify a subscriber when the changed (signal) name is a path ancestor of the subscribed name:

exact
  ? currentName === signalName || currentName.startsWith(signalName + '.')
  : currentName.startsWith(signalName) || signalName.startsWith(currentName)

The + '.' boundary keeps siblings and substring-only names from matching (e.g. data does not match database, and data.other does not match data.type). Descendant-only signals are still excluded under exact matching, preserving the documented purpose of exact.

Validation

  • New integration test in useController.test.tsx: after setValue('data', { type: 'foo' }) then setValue('data', null), the controlled value becomes undefined and stays in sync with useWatch.
  • New unit cases in shouldSubscribeByName.test.ts covering ancestor matches and the sibling/substring exclusions (data vs database).
  • pnpm test (full suite): 1182 passed.
  • Targeted: shouldSubscribeByName, useController, useWatch, controller, setValue, useFormState — all green.
  • eslint clean on changed files; tsc --noEmit passes.

Not a duplicate

There are no open PRs addressing #13550 or the exact-subscription ancestor-notification gap. The change is limited to shouldSubscribeByName (single conditional) plus tests; no public API or exact semantics for sibling/descendant matching are altered.

Copilot AI and others added 4 commits June 26, 2026 15:58
When a parent object field is replaced (e.g. set to null via setValue),
controlled fields subscribed to a nested path were not notified because
useController subscribes with exact: true and shouldSubscribeByName only
matched on strict name equality. As a result the controlled value went out
of sync with form state (getValues/useWatch).

Make exact matching also notify subscribers when an ancestor path changes
(currentName starts with signalName + '.'), while still excluding sibling
and substring-only names (e.g. data vs database).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bluebill1049
bluebill1049 merged commit 792ab40 into react-hook-form:master Jun 30, 2026
6 checks passed
KATT added a commit to KATT/react-hook-form that referenced this pull request Jul 3, 2026
…bmitting-cypress-jest

* origin/master:
  🐞 fix(flatten): preserve Date values as leaf nodes (react-hook-form#13566)
  Revert "docs: fix grammar in demo descriptions (react-hook-form#13565)" (react-hook-form#13568)
  Revert "test: remove duplicate UseFieldArray slug (react-hook-form#13564)" (react-hook-form#13567)
  test: remove duplicate UseFieldArray slug (react-hook-form#13564)
  docs: fix grammar in demo descriptions (react-hook-form#13565)
  🐞 fix(useController): reflect cleared parent object in controlled fields (react-hook-form#13550) (react-hook-form#13553)
  📖 docs: replace dead /api links with /docs in locale READMEs (react-hook-form#13561)
  🐛 fix(unset): guard against prototype keyword path traversal (react-hook-form#13559) (react-hook-form#13560)
  📖 docs: replace retired /jp, /pt, /zh subdomain links in locale READMEs (react-hook-form#13556)
KATT added a commit to KATT/react-hook-form that referenced this pull request Jul 3, 2026
…ssubmitting

* repro/activity-issubmitting-vitest:
  🐞 fix(flatten): preserve Date values as leaf nodes (react-hook-form#13566)
  Revert "docs: fix grammar in demo descriptions (react-hook-form#13565)" (react-hook-form#13568)
  Revert "test: remove duplicate UseFieldArray slug (react-hook-form#13564)" (react-hook-form#13567)
  test: remove duplicate UseFieldArray slug (react-hook-form#13564)
  docs: fix grammar in demo descriptions (react-hook-form#13565)
  🐞 fix(useController): reflect cleared parent object in controlled fields (react-hook-form#13550) (react-hook-form#13553)
  📖 docs: replace dead /api links with /docs in locale READMEs (react-hook-form#13561)
  🐛 fix(unset): guard against prototype keyword path traversal (react-hook-form#13559) (react-hook-form#13560)
  📖 docs: replace retired /jp, /pt, /zh subdomain links in locale READMEs (react-hook-form#13556)
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.

issue: Clearing a value that is an object does not remove the value from useController

3 participants