Skip to content

fix: prevent initialFormState from overriding client values during merge#2096

Open
Mishra-coder wants to merge 3 commits intoTanStack:mainfrom
Mishra-coder:fix/issue-2089-initial-form-state-override
Open

fix: prevent initialFormState from overriding client values during merge#2096
Mishra-coder wants to merge 3 commits intoTanStack:mainfrom
Mishra-coder:fix/issue-2089-initial-form-state-override

Conversation

@Mishra-coder
Copy link
Copy Markdown

@Mishra-coder Mishra-coder commented Mar 26, 2026

The initialFormState exported by the Next.js and Remix adapters contained a values: undefined property. During the server-client state merge process (specifically in mutateMergeDeep), this undefined value was being merged into the client's form state, causing existing field values to be reset to undefined after initial mount or during route transitions.

This fix removes the values property from the initialFormState object in both adapters. By omitting the property entirely, the merge logic no longer overwrites existing client values when no server-side values are provided.

This approach is safe because:

  • values: undefined explicitly overrides client state
  • omitting values preserves existing client values
  • server-provided values (e.g. after validation) will still correctly override client state as expected

To maintain type compatibility, a TypeScript cast is used:
as unknown as ServerFormState<any, undefined>

This resolves the issue where forms using server actions would have their fields set to undefined after initial render or during client-side navigation.

Closes #2089

Summary by CodeRabbit

  • Refactor
    • Streamlined form state initialization and type handling across packages to simplify runtime shape while preserving compatibility.
  • Tests
    • Added test suites verifying initial server form state omits client values and that merging server state into client state does not overwrite existing form values.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The initialFormState in both server-validation modules now omits the runtime values property and uses a type assertion to satisfy ServerFormState<any, undefined>. Added tests in each package verifying initialFormState lacks values and does not overwrite client values when merged.

Changes

Cohort / File(s) Summary
Server Form State Initialization
packages/react-form-nextjs/src/createServerValidate.ts, packages/react-form-remix/src/createServerValidate.ts
Removed explicit values: undefined from the runtime object; added as unknown as ServerFormState<any, undefined> type assertion to preserve compile-time type.
Tests for initialFormState merge behavior
packages/react-form-nextjs/tests/createServerValidate.spec.tsx, packages/react-form-remix/tests/createServerValidate.spec.tsx
New Vitest specs added to assert initialFormState has no values key and that deep-merging it into a client state does not overwrite existing clientState.values.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I nudge the state with gentle paws,
values hidden without a cause,
types declared, the runtime light,
merges keep the client right,
a quiet hop through code tonight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: prevent initialFormState from overriding client values during merge' accurately and specifically describes the main change in the PR.
Description check ✅ Passed The PR description comprehensively explains the bug, the fix approach, type safety reasoning, and closes the linked issue, though the checklist is not marked as completed.
Linked Issues check ✅ Passed The code changes fully address issue #2089 by removing the values property from initialFormState in both Next.js and Remix adapters and adding regression tests.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing issue #2089: modifications to createServerValidate.ts files and corresponding regression tests in both adapters.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 29, 2026

View your CI Pipeline Execution ↗ for commit d6a7af9

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 1m 2s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-29 09:51:05 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 29, 2026

More templates

@tanstack/angular-form

npm i https://pkg.pr.new/@tanstack/angular-form@2096

@tanstack/form-core

npm i https://pkg.pr.new/@tanstack/form-core@2096

@tanstack/form-devtools

npm i https://pkg.pr.new/@tanstack/form-devtools@2096

@tanstack/lit-form

npm i https://pkg.pr.new/@tanstack/lit-form@2096

@tanstack/react-form

npm i https://pkg.pr.new/@tanstack/react-form@2096

@tanstack/react-form-devtools

npm i https://pkg.pr.new/@tanstack/react-form-devtools@2096

@tanstack/react-form-nextjs

npm i https://pkg.pr.new/@tanstack/react-form-nextjs@2096

@tanstack/react-form-remix

npm i https://pkg.pr.new/@tanstack/react-form-remix@2096

@tanstack/react-form-start

npm i https://pkg.pr.new/@tanstack/react-form-start@2096

@tanstack/solid-form

npm i https://pkg.pr.new/@tanstack/solid-form@2096

@tanstack/solid-form-devtools

npm i https://pkg.pr.new/@tanstack/solid-form-devtools@2096

@tanstack/svelte-form

npm i https://pkg.pr.new/@tanstack/svelte-form@2096

@tanstack/vue-form

npm i https://pkg.pr.new/@tanstack/vue-form@2096

commit: d6a7af9

@sentry
Copy link
Copy Markdown

sentry bot commented Mar 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (6892ed0) to head (d6a7af9).
⚠️ Report is 159 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #2096       +/-   ##
==========================================
- Coverage   90.35%   0.00%   -90.36%     
==========================================
  Files          38       6       -32     
  Lines        1752      50     -1702     
  Branches      444      18      -426     
==========================================
- Hits         1583       0     -1583     
+ Misses        149      42      -107     
+ Partials       20       8       -12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/react-form-nextjs/tests/createServerValidate.spec.tsx`:
- Around line 17-18: Add a descriptive reason to the existing TypeScript
suppression above the mutateMergeDeep(clientState, initialFormState) call:
replace the bare "// `@ts-ignore`" with a comment that includes the TS error code
and a short justification (e.g., indicating this is intentional test-only
mismatch of types), or alternatively use "// eslint-disable-next-line
`@typescript-eslint/ban-ts-comment` -- <reason>" style; ensure the comment
directly precedes the mutateMergeDeep invocation so ESLint recognizes the
description.

In `@packages/react-form-remix/tests/createServerValidate.spec.tsx`:
- Around line 17-18: Replace the bare "// `@ts-ignore`" above the call to
mutateMergeDeep(clientState, initialFormState) with a documented suppression or
preferred directive: either use "// `@ts-expect-error` -- <reason why types are
incompatible at runtime>" to ensure the suppression fails if types become
compatible, or keep "// `@ts-ignore` -- <explicit reason>" explaining why the type
error is acceptable here; make sure the comment references the mutateMergeDeep
call and why clientState/initialFormState type mismatch is intentionally
ignored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8398ee9b-ed6b-43a1-8cd2-af200314aca2

📥 Commits

Reviewing files that changed from the base of the PR and between d6a7af9 and 4d8e0ab.

📒 Files selected for processing (2)
  • packages/react-form-nextjs/tests/createServerValidate.spec.tsx
  • packages/react-form-remix/tests/createServerValidate.spec.tsx

Comment on lines +17 to +18
// @ts-ignore
mutateMergeDeep(clientState, initialFormState)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add description to @ts-ignore directive.

Same issue as in the Remix test file. ESLint requires a description for the type suppression.

Proposed fix
-    // `@ts-ignore`
+    // `@ts-expect-error` - Testing with simplified mock state that doesn't match full ServerFormState type
     mutateMergeDeep(clientState, initialFormState)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @ts-ignore
mutateMergeDeep(clientState, initialFormState)
// `@ts-expect-error` - Testing with simplified mock state that doesn't match full ServerFormState type
mutateMergeDeep(clientState, initialFormState)
🧰 Tools
🪛 ESLint

[error] 17-17: Include a description after the "@ts-ignore" directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer.

(@typescript-eslint/ban-ts-comment)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-form-nextjs/tests/createServerValidate.spec.tsx` around lines
17 - 18, Add a descriptive reason to the existing TypeScript suppression above
the mutateMergeDeep(clientState, initialFormState) call: replace the bare "//
`@ts-ignore`" with a comment that includes the TS error code and a short
justification (e.g., indicating this is intentional test-only mismatch of
types), or alternatively use "// eslint-disable-next-line
`@typescript-eslint/ban-ts-comment` -- <reason>" style; ensure the comment
directly precedes the mutateMergeDeep invocation so ESLint recognizes the
description.

Comment on lines +17 to +18
// @ts-ignore
mutateMergeDeep(clientState, initialFormState)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add description to @ts-ignore directive.

ESLint requires a description explaining why the type suppression is necessary. Consider using @ts-expect-error which is preferred as it will error if the suppression becomes unnecessary.

Proposed fix
-    // `@ts-ignore`
+    // `@ts-expect-error` - Testing with simplified mock state that doesn't match full ServerFormState type
     mutateMergeDeep(clientState, initialFormState)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @ts-ignore
mutateMergeDeep(clientState, initialFormState)
// `@ts-expect-error` - Testing with simplified mock state that doesn't match full ServerFormState type
mutateMergeDeep(clientState, initialFormState)
🧰 Tools
🪛 ESLint

[error] 17-17: Include a description after the "@ts-ignore" directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer.

(@typescript-eslint/ban-ts-comment)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-form-remix/tests/createServerValidate.spec.tsx` around lines
17 - 18, Replace the bare "// `@ts-ignore`" above the call to
mutateMergeDeep(clientState, initialFormState) with a documented suppression or
preferred directive: either use "// `@ts-expect-error` -- <reason why types are
incompatible at runtime>" to ensure the suppression fails if types become
compatible, or keep "// `@ts-ignore` -- <explicit reason>" explaining why the type
error is acceptable here; make sure the comment references the mutateMergeDeep
call and why clientState/initialFormState type mismatch is intentionally
ignored.

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.

Using next.js server actions, fields are set to undefined

1 participant