diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 2af502b1b..21a269457 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -993,11 +993,16 @@ export class FieldApi< this.form = opts.form as never this.name = opts.name as never this.timeoutIds = {} as Record + // Only really relevant for arrays, the value could be gone once Derived's update fn is triggered + const potentialPreviousValue = this.form.getFieldValue(this.name) this.store = new Derived({ deps: [this.form.store], fn: () => { - const value = this.form.getFieldValue(this.name) + let value = this.form.getFieldValue(this.name) + if (value === undefined && potentialPreviousValue !== undefined) { + value = potentialPreviousValue + } const meta = this.form.getFieldMeta(this.name) ?? { ...defaultFieldMeta, ...opts.defaultMeta, diff --git a/packages/react-form/tests/useField.test.tsx b/packages/react-form/tests/useField.test.tsx index de58b87da..5f5781f79 100644 --- a/packages/react-form/tests/useField.test.tsx +++ b/packages/react-form/tests/useField.test.tsx @@ -1165,6 +1165,77 @@ describe('useField', () => { expect(queryByText(fakePeople.molly.name)).not.toBeInTheDocument() }) + it('should not make field uncontrolled during array item removal', async () => { + // Spy on console.error before rendering + const consoleErrorSpy = vi.spyOn(console, 'error') + + let id = 0 + + function Comp() { + const form = useForm({ + defaultValues: { + people: [] as { id: number; name: string }[], + }, + }) + + return ( +
+ + {(people) => ( +
+ + {people.state.value.map((person, i) => ( + + {(field) => { + return ( +
+ { + field.handleChange(e.target.value) + }} + /> + +
+ ) + }} +
+ ))} +
+ )} +
+
+ ) + } + + const { getByTestId } = render() + await user.click(getByTestId('add')) + await user.click(getByTestId('add')) + await user.click(getByTestId('remove-0')) + // Making a controlled field uncontrolled will log an error + expect(consoleErrorSpy).not.toHaveBeenCalled() + }) + it('should not rerender unrelated fields', async () => { const renderCount = { field1: 0,