diff --git a/.changeset/fix-array-reset-rerender.md b/.changeset/fix-array-reset-rerender.md new file mode 100644 index 000000000..a9660b655 --- /dev/null +++ b/.changeset/fix-array-reset-rerender.md @@ -0,0 +1,8 @@ +--- +'@tanstack/form-core': patch +'@tanstack/react-form': patch +--- + +Bump `_arrayVersion` when resetting array fields so adapters re-render after `form.reset()` or `form.resetField()` changes array length. + +Fixes #2228 diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index d710cd105..0c8c7a8a5 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1847,6 +1847,15 @@ export class FormApi< fieldMetaBase, }) }) + + const helper = metaHelper(this) + for (const fieldKey of Object.keys( + this.fieldInfo, + ) as DeepKeys[]) { + if (Array.isArray(this.getFieldValue(fieldKey))) { + helper.bumpArrayVersion(fieldKey) + } + } } /** @@ -2954,6 +2963,10 @@ export class FormApi< : prev.values, } }) + + if (Array.isArray(this.getFieldValue(field))) { + metaHelper(this).bumpArrayVersion(field) + } } /** diff --git a/packages/form-core/tests/FormApi.spec.ts b/packages/form-core/tests/FormApi.spec.ts index 024b319f0..53d65ff7e 100644 --- a/packages/form-core/tests/FormApi.spec.ts +++ b/packages/form-core/tests/FormApi.spec.ts @@ -125,6 +125,23 @@ describe('form api', () => { }) }) + it('should bump array version when resetting to a shorter array', () => { + const form = new FormApi({ + defaultValues: { + items: [1, 2, 3], + }, + }) + form.mount() + + const field = new FieldApi({ form, name: 'items' }) + field.mount() + + form.reset({ items: [4] }) + + expect(form.getFieldValue('items')).toEqual([4]) + expect(form.getFieldMeta('items')?._arrayVersion).toBe(1) + }) + it('form should reset default value when resetting in onSubmit', async () => { const defaultValues = { name: '', diff --git a/packages/react-form/tests/useField.test.tsx b/packages/react-form/tests/useField.test.tsx index 445416594..aff1e06ad 100644 --- a/packages/react-form/tests/useField.test.tsx +++ b/packages/react-form/tests/useField.test.tsx @@ -1644,6 +1644,48 @@ describe('useField', () => { expect(getByTestId('item-0')).toHaveTextContent('Alice') }) + it('should rerender array field when reset to a shorter array', async () => { + // Regression test for https://github.com/TanStack/form/issues/2228 + function Comp() { + const form = useForm({ + defaultValues: { + items: [1, 2, 3], + }, + }) + + return ( + <> + + + {(field) => ( +
    + {field.state.value.map((item, i) => ( +
  1. + {item} +
  2. + ))} +
+ )} +
+ + ) + } + + const { getByTestId } = render() + expect(getByTestId('list').children).toHaveLength(3) + + await user.click(getByTestId('reset')) + + expect(getByTestId('list').children).toHaveLength(1) + expect(getByTestId('item-0')).toHaveTextContent('4') + }) + it('should handle defaultValue without setstate-in-render error', async () => { // Spy on console.error before rendering const consoleErrorSpy = vi.spyOn(console, 'error')