Skip to content

fix(design): avoid stack overflow on cyclic Form field values - #1576

Merged
dengfuping merged 1 commit into
masterfrom
fix/form-revalidate-cyclic-values
Sep 10, 2026
Merged

dengfuping merged 1 commit into
masterfrom
fix/form-revalidate-cyclic-values

Conversation

@dengfuping

@dengfuping dengfuping commented Sep 10, 2026 •

Copy link
Copy Markdown
Collaborator

📦 Modified package

  • @oceanbase/design
  • @oceanbase/ui
  • @oceanbase/icons
  • @oceanbase/charts
  • @oceanbase/util
  • @oceanbase/codemod
  • Other (about what?)

🤔 This is a ...

  • New feature
  • Bug fix
  • Site / documentation update
  • Demo update
  • Component style update
  • TypeScript definition update
  • Bundle size optimization
  • Performance optimization
  • Enhancement feature
  • Internationalization
  • Refactoring
  • Code style optimization
  • Test Case
  • Branch merge
  • Workflow
  • Other (about what?)

🔗 Related issue link

N/A

💡 Background and solution

With the default validateMode="onSubmit" + reValidateMode="onChange", Form injected a revalidation step into onValuesChange. To know which fields had changed, it recursively walked the changed values (getChangedNamePaths), a walk with no cycle detection:

const cyclic = {};
cyclic.self = cyclic;
getChangedNamePaths({ field: cyclic }); // RangeError: Maximum call stack size exceeded

Changed values are often non-plain in practice — e.g. a labelInValue Select whose label is a React node, and a React element's _owner points back into the fiber graph. Every field change then threw before the consumer's own onValuesChange ran. The walk also ran on every change even when its result was discarded (before the first submit, only fields that already show errors get revalidated).

Solution

  • Take the changed names from rc-field-form's onFieldsChange (changedFields[].name) instead of re-deriving them from values, so the consumer's onValuesChange is passed through untouched. A field counts as changed only when its value differs from the last value seen for it (identity comparison, mirroring rc-field-form's own newValue !== value detection), which also filters out the validation-state updates that changedFields carries.
  • Move the tracking state (blurred fields / last values / submitted) from component refs into state keyed by the form instance, and patch resetFields() so a full reset clears everything while a partial resetFields(['name']) re-seeds that field. This keeps the reset semantics (submitted cleared on full reset only) when the same Form.useForm() instance is reused across a Form unmount / remount.
  • onTouched now marks a field as touched on blur instead of on the first change: a change reports touched: true without validating, so touched cannot tell typing apart from blur. Fields without rules are unaffected as they have nothing to validate.
  • The validateMode demo declares its mode union locally instead of importing the type from the package root, so the package root keeps exporting only FormProps / FormItemProps for Form.

📝 Changelog

Language Changelog
🇺🇸 English - Form
  - 🐞 Fixed the injected revalidation throwing RangeError: Maximum call stack size exceeded when a changed value contains a circular reference, such as a labelInValue Select whose label is a React node.
  - 🐞 onValuesChange is no longer wrapped, so the consumer callback always runs when the injected revalidation is active.
  - 🐞 Fixed resetFields() not clearing the submitted state: after a full reset the field counts as untouched and does not revalidate on change, including when the Form unmounts and remounts with the same Form.useForm() instance.
🇨🇳 Chinese - Form
  - 🐞 修复注入的校验在变更值包含循环引用时抛出 RangeError: Maximum call stack size exceeded 的问题,例如 labelInValue 的 Select 以 React 节点作为 label。
  - 🐞 不再包装 onValuesChange,注入校验生效时用户回调也会正常执行。
  - 🐞 修复 resetFields() 未重置提交状态的问题:完整重置后字段视为未触碰,变更时不再触发校验;Form 卸载后使用同一 Form.useForm() 实例重新挂载时同样成立。

☑️ Self-Check before Merge

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • TypeScript definition is updated/provided or not needed
  • Tests is updated/provided or not needed
  • Changelog is provided or not needed

@vercel

vercel Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
oceanbase-design Ready Ready Preview Sep 10, 2026 9:28am UTC

@github-actions

github-actions Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

⚡️ Preview Deploying...

@dengfuping
dengfuping force-pushed the fix/form-revalidate-cyclic-values branch from b809e49 to 9277325 Compare September 10, 2026 09:23
@dengfuping
dengfuping merged commit 7df99f7 into master Sep 10, 2026
8 of 9 checks passed

This branch was successfully deployed

1 active deployment
Preview — 92773256 Deployed Sep 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working @oceanbase/design

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant