Skip to content

fix: preserve form values during StrictMode remounts - #812

Open
Rober1208 wants to merge 2 commits into
react-component:masterfrom
Rober1208:codex/fix-clear-on-destroy-strictmode
Open

Rober1208 wants to merge 2 commits into
react-component:masterfrom
Rober1208:codex/fix-clear-on-destroy-strictmode

Conversation

@Rober1208

@Rober1208 Rober1208 commented Oct 1, 2026 •

Copy link
Copy Markdown

With clearOnDestroy enabled, StrictMode's mount replay clears the form store without initializing it again. Initial values and values set before fields mount disappear from getFieldsValue(true); a later rerender or delayed field mount displays an empty value. Related to ant-design/ant-design#54467.

Capture the store before fields unregister and restore it before their mount callbacks during replay, using a component that renders no DOM. Explicit value updates or resets after cleanup take precedence over the saved snapshot; internal field initialization and metadata-only updates do not cancel recovery. Named fields refresh when the mount-time value differs from their last rendered value, keeping the displayed value consistent with the recovered store. A genuine unmount still clears the store through the existing cleanup, and a new mount starts with its initial values.

Validation:

  • The focused suite passes all 27 tests. The unchanged upstream base fails 14; the earlier PR commit fails the six added recovery regressions. Coverage includes delayed/preloaded fields, layout effects, watchers, controlled fields, lists, callbacks, genuine unmount/remounts, child reconciliation and Activity's delayed first mount.
  • Activity regressions cover setFieldsValue, setFieldValue, value-bearing setFields, resetFields, hidden field initialization, metadata-only updates and the unchanged ordinary-mount render count.
  • npm test -- --coverage: 24 test files passed, 229 tests passed, 1 existing skipped test.
  • npm run lint, npm run tsc, npm run compile and formatting checks passed.
  • React 19.3 SSR checks preserve the base markup without warnings in development and production. A separate renderer comparison without DOM globals verifies recovered values, displayed values and genuine unmount cleanup.

Local installation used npm install --legacy-peer-deps because the unchanged package manifest pairs ESLint 10 with eslint-plugin-react's ESLint 9 peer range. No dependency or lockfile changes are included. The local commands exercise the project's scripts; upstream CI has not run yet.

React 16.9 runtime verification was blocked on both the base and the fix by the installed @rc-component/util importing react-dom/client. No React Native host integration test was run.

Summary by CodeRabbit

  • 修复
    • 修复表单在 React StrictMode 重放或组件暂时隐藏后,初始值和预加载值可能丢失的问题。
    • 隐藏期间进行的表单更新会在组件重新显示后保留;表单实际卸载时仍会按配置清空数据。
    • 修复字段挂载时未及时显示最新表单值的情况。

Copilot AI balanced review requested due to automatic review settings October 1, 2026 09:08
@vercel

vercel Bot commented Oct 1, 2026

Copy link
Copy Markdown

@Rober1208 is attempting to deploy a commit to the React Component Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 43054167-3739-49fd-9361-53013c95e349

📥 Commits

Reviewing files that changed from the base of the PR and between 218eb27 and 6889399.

📒 Files selected for processing (3)
  • src/Field.tsx
  • src/hooks/useForm.ts
  • tests/clearOnDestroy.test.tsx

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


Walkthrough

新增内部 Store 备份与恢复机制。表单根据首次挂载时的 clearOnDestroy 值处理 Store。显式值更新会阻止旧 Store 恢复。字段挂载时会检查当前值与最后一次渲染值是否一致。

Changes

表单 Store 恢复

Layer / File(s) Summary
Store 备份回调与写入版本
src/interface.ts, src/hooks/useForm.ts, src/FieldContext.ts
InternalHooks 新增 backupStore。回调捕获当前 Store 和显式值更新版本,并仅在版本未变化时恢复 Store。resetFields、含 value 的 setFields、updateValue 和传入非空值的 setFieldsValue 会递增版本。
表单生命周期集成与验证
src/Form.tsx, tests/clearOnDestroy.test.tsx
FormStoreSnapshot 缓存首次挂载时的 clearOnDestroy 值,并在字段上下文提供器之前执行待恢复回调。新增测试覆盖 Activity、StrictMode、延迟挂载、布局 effect、受控字段、列表及真实卸载后的重新挂载。
字段挂载时的值检查
src/Field.tsx
Field 记录最后一次渲染的值。挂载后,具名字段在当前值与最后一次渲染值不同时触发重渲染。

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Form
  participant FormStoreSnapshot
  participant FormStore
  Form->>FormStoreSnapshot: 传入 backupStore 和 clearOnDestroy
  FormStoreSnapshot->>FormStore: 卸载时调用 backupStore()
  FormStore-->>FormStoreSnapshot: 返回条件恢复回调
  FormStoreSnapshot->>FormStore: 挂载时调用恢复回调
Loading

Merge Risk: ⚪ Minimal · up to 68893

The change preserves form values across StrictMode and Activity replays with explicit-write guards and broad regression tests. No actionable merge-blocking issue remains.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 68893

Recovery respects explicit value updates, and ordinary unmount cleanup remains intact. A narrow ownership risk remains if multiple overlapping forms share one instance: replay could restore values another form has cleared.

Retained concerns

  • Low · architecture · inferred: If overlapping Form components share one FormInstance, one component's pending snapshot can remain eligible after another component performs clearOnDestroy cleanup. A subsequent replay could restore cleared values through existing fields. Recovery has no lifecycle-owner coordination; support for this usage and its actual exposure remain unestablished.
Security review details

Security Blast Radius

  • inferred — The identified recovery exposure is bounded to values in the supplied FormInstance and its existing consumers. Separate instances create separate FormStores; sharing an instance already shares its values and callbacks, rather than establishing an isolation boundary.

Security Findings and Attack Paths

  • inferred — The conditional path is a pending shared-store snapshot, another lifecycle owner's cleanup, and later replay into existing field consumers. It requires overlapping application-controlled lifecycles; no remote attacker trigger, additional privilege, or cross-tenant recipient was established.

Trust Boundaries and Controls

  • observed — Recovered values continue through the existing caller-provided controlled children and render callbacks. The new mount comparison changes when those paths refresh, not their recipients or authority. Explicit value mutations invalidate recovery.

Resilience and Maintainability Implications

  • inferred — Failure containment is clearest with one active Form lifecycle owner per FormInstance. Multiple owners receive independent recovery callbacks but share the mutation version and cleanup target, leaving recovery precedence across owners undefined.

Hardening Proposals

  • proposed — Define and enforce a single active lifecycle owner per FormInstance, or, if simultaneous sharing is supported, coordinate recovery by owner and lifecycle generation so another owner's terminal cleanup cannot be undone by a pending snapshot.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了主要变更:在 StrictMode 重挂载期间保留表单值。标题简洁、明确,并与变更内容直接相关。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

小兔捧来一枚表单值,
Store 入梦,回调守候。
挂载时轻轻唤醒它,
写入更新便留新值。
字段睁眼,读见初始值,
小兔蹦跳,庆祝恢复。

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

@Rober1208
Rober1208 marked this pull request as draft October 1, 2026 09:34
@Rober1208
Rober1208 marked this pull request as ready for review October 1, 2026 10:11

This branch has not been deployed

No deployments
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