From 0c8b4e584198e502c7cad6a709479df03ee68b83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 4 Mar 2026 13:21:02 +0800 Subject: [PATCH 1/3] feat: add delayFrame option to validateFields - Add delayFrame option to InternalValidateOptions interface - Conditionally call delayFrame when delayFrame option is true in Field.validateRules - Use delayFrame: true in triggerDependenciesUpdate to avoid useWatch rule timing issues - Adjust test files (jest.mock formatting cleanup) --- src/Field.tsx | 6 ++++-- src/hooks/useForm.ts | 5 ++++- src/interface.ts | 1 + tests/index.test.tsx | 2 +- tests/list.test.tsx | 2 +- tests/validate-warning.test.tsx | 2 +- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Field.tsx b/src/Field.tsx index be5a13c2..7aaa266d 100644 --- a/src/Field.tsx +++ b/src/Field.tsx @@ -392,7 +392,7 @@ class Field extends React.Component implements F const namePath = this.getNamePath(); const currentValue = this.getValue(); - const { triggerName, validateOnly = false } = options || {}; + const { triggerName, validateOnly = false, delayFrame: showDelayFrame } = options || {}; // Force change to async to avoid rule OOD under renderProps field const rootPromise = Promise.resolve().then(async (): Promise => { @@ -404,7 +404,9 @@ class Field extends React.Component implements F // Should wait for the frame render, // since developer may `useWatch` value in the rules. - await delayFrame(); + if (showDelayFrame) { + await delayFrame(); + } // Start validate let filteredRules = this.getRules(); diff --git a/src/hooks/useForm.ts b/src/hooks/useForm.ts index 12f99f9d..ebbefd92 100644 --- a/src/hooks/useForm.ts +++ b/src/hooks/useForm.ts @@ -747,7 +747,10 @@ export class FormStore { private triggerDependenciesUpdate = (prevStore: Store, namePath: InternalNamePath) => { const childrenFields = this.getDependencyChildrenFields(namePath); if (childrenFields.length) { - this.validateFields(childrenFields); + this.validateFields(childrenFields, { + // Delay to avoid `useWatch` dynamic adjust rules that deps not get latest one + delayFrame: true, + }); } this.notifyObservers(prevStore, childrenFields, { diff --git a/src/interface.ts b/src/interface.ts index f533be61..f18799a8 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -157,6 +157,7 @@ export type ValidateFields = { export interface InternalValidateOptions extends ValidateOptions { triggerName?: string; validateMessages?: ValidateMessages; + delayFrame?: boolean; } export type InternalValidateFields = { diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 8cafc91f..f3d0c7d5 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -7,8 +7,8 @@ import { changeValue, getInput, matchError } from './common'; import InfoField, { Input } from './common/InfoField'; import timeout, { waitFakeTime } from './common/timeout'; + jest.mock('../src/utils/delayUtil'); -import type { FormRef, Meta } from '@/interface'; describe('Form.Basic', () => { describe('create form', () => { diff --git a/tests/list.test.tsx b/tests/list.test.tsx index a491b83a..576b5e76 100644 --- a/tests/list.test.tsx +++ b/tests/list.test.tsx @@ -10,8 +10,8 @@ import InfoField, { Input } from './common/InfoField'; import { changeValue, getInput } from './common'; import timeout from './common/timeout'; -jest.mock('../src/utils/delayUtil'); +jest.mock('../src/utils/delayUtil'); describe('Form.List', () => { const form = React.createRef(); diff --git a/tests/validate-warning.test.tsx b/tests/validate-warning.test.tsx index 4a142c40..4c07ffdb 100644 --- a/tests/validate-warning.test.tsx +++ b/tests/validate-warning.test.tsx @@ -5,8 +5,8 @@ import { changeValue, getInput, matchError } from './common'; import type { FormInstance, Rule } from '../src/interface'; import { render } from '@testing-library/react'; -jest.mock('../src/utils/delayUtil'); +jest.mock('../src/utils/delayUtil'); describe('Form.WarningValidate', () => { it('required', async () => { const form = React.createRef(); From 15f25392467d5bcf8102539f3163d41aa912260f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 4 Mar 2026 15:05:23 +0800 Subject: [PATCH 2/3] chore: remove delayUtil jest mock from tests The jest.mock('../src/utils/delayUtil') was removed from multiple test files since it's no longer needed after the delayFrame implementation. Co-Authored-By: Claude Opus 4.6 --- tests/context.test.tsx | 2 -- tests/index.test.tsx | 3 --- tests/list.test.tsx | 2 -- tests/validate-warning.test.tsx | 2 -- 4 files changed, 9 deletions(-) diff --git a/tests/context.test.tsx b/tests/context.test.tsx index 2a68ab13..4a587c9f 100644 --- a/tests/context.test.tsx +++ b/tests/context.test.tsx @@ -6,8 +6,6 @@ import InfoField from './common/InfoField'; import { changeValue, matchError, getInput } from './common'; import timeout from './common/timeout'; -jest.mock('../src/utils/delayUtil'); - describe('Form.Context', () => { it('validateMessages', async () => { const { container } = render( diff --git a/tests/index.test.tsx b/tests/index.test.tsx index f3d0c7d5..b27917a2 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -7,9 +7,6 @@ import { changeValue, getInput, matchError } from './common'; import InfoField, { Input } from './common/InfoField'; import timeout, { waitFakeTime } from './common/timeout'; - -jest.mock('../src/utils/delayUtil'); - describe('Form.Basic', () => { describe('create form', () => { const Content: React.FC = () => ( diff --git a/tests/list.test.tsx b/tests/list.test.tsx index 576b5e76..9aac91b3 100644 --- a/tests/list.test.tsx +++ b/tests/list.test.tsx @@ -10,8 +10,6 @@ import InfoField, { Input } from './common/InfoField'; import { changeValue, getInput } from './common'; import timeout from './common/timeout'; - -jest.mock('../src/utils/delayUtil'); describe('Form.List', () => { const form = React.createRef(); diff --git a/tests/validate-warning.test.tsx b/tests/validate-warning.test.tsx index 4c07ffdb..84b0b6a8 100644 --- a/tests/validate-warning.test.tsx +++ b/tests/validate-warning.test.tsx @@ -5,8 +5,6 @@ import { changeValue, getInput, matchError } from './common'; import type { FormInstance, Rule } from '../src/interface'; import { render } from '@testing-library/react'; - -jest.mock('../src/utils/delayUtil'); describe('Form.WarningValidate', () => { it('required', async () => { const form = React.createRef(); From 7a40329355f3c29a7430935f0d4f570f4da89719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 4 Mar 2026 15:14:18 +0800 Subject: [PATCH 3/3] fix: restore missing type imports in tests --- tests/index.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/index.test.tsx b/tests/index.test.tsx index b27917a2..f6ab2dbf 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -7,6 +7,7 @@ import { changeValue, getInput, matchError } from './common'; import InfoField, { Input } from './common/InfoField'; import timeout, { waitFakeTime } from './common/timeout'; +import type { FormRef, Meta } from '@/interface'; describe('Form.Basic', () => { describe('create form', () => { const Content: React.FC = () => (