diff --git a/.changeset/quiet-fields-revalidate.md b/.changeset/quiet-fields-revalidate.md new file mode 100644 index 0000000000..cc0ea4fe60 --- /dev/null +++ b/.changeset/quiet-fields-revalidate.md @@ -0,0 +1,5 @@ +--- +'@tanstack/form-core': patch +--- + +Set `isValidating` on every async field validation run, not only the first one. diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 80770f8897..a57ebca45d 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1513,6 +1513,7 @@ export class FieldApi< field.timeoutIds.validations[validateObj.cause] = setTimeout( async () => { + field.timeoutIds.validations[validateObj.cause] = null if (controller.signal.aborted) return rawResolve(undefined) try { rawResolve( diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 7c9a468ab8..ee517aa8b4 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -892,6 +892,46 @@ describe('field api', () => { storeunsub() }) + it('should set isValidating on every async validation run', async () => { + // Test for https://github.com/TanStack/form/issues/2372 + vi.useFakeTimers() + + const form = new FormApi({ + defaultValues: { + name: '', + }, + }) + + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + validators: { + onChangeAsync: async ({ value }) => { + await sleep(1000) + return value === 'admin' ? 'Username is already taken' : undefined + }, + }, + }) + + field.mount() + + field.setValue('admin') + await vi.advanceTimersByTimeAsync(0) + expect(field.getMeta().isValidating).toBe(true) + await vi.runAllTimersAsync() + expect(field.getMeta().isValidating).toBe(false) + expect(field.getMeta().errors).toStrictEqual(['Username is already taken']) + + field.setValue('asdf') + await vi.advanceTimersByTimeAsync(0) + expect(field.getMeta().isValidating).toBe(true) + await vi.runAllTimersAsync() + expect(field.getMeta().isValidating).toBe(false) + expect(field.getMeta().errors).toStrictEqual([]) + }) + it('should run async validation onChange', async () => { vi.useFakeTimers()