Skip to content

Commit 0a78476

Browse files
authored
fix(react-form): removing array items (#1319)
1 parent ec683bd commit 0a78476

File tree

3 files changed

+124
-24
lines changed

3 files changed

+124
-24
lines changed

docs/reference/classes/fieldapi.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ The current field state.
163163
getInfo(): FieldInfo<TParentData>
164164
```
165165

166-
Defined in: [packages/form-core/src/FieldApi.ts:1217](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1217)
166+
Defined in: [packages/form-core/src/FieldApi.ts:1222](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1222)
167167

168168
Gets the field information object.
169169

@@ -179,7 +179,7 @@ Gets the field information object.
179179
getMeta(): FieldMeta<TParentData, TName, TData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TFormOnMount, TFormOnChange, TFormOnChangeAsync, TFormOnBlur, TFormOnBlurAsync, TFormOnSubmit, TFormOnSubmitAsync>
180180
```
181181

182-
Defined in: [packages/form-core/src/FieldApi.ts:1185](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1185)
182+
Defined in: [packages/form-core/src/FieldApi.ts:1190](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1190)
183183

184184
#### Returns
185185

@@ -193,7 +193,7 @@ Defined in: [packages/form-core/src/FieldApi.ts:1185](https://github.com/TanStac
193193
getValue(): TData
194194
```
195195

196-
Defined in: [packages/form-core/src/FieldApi.ts:1167](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1167)
196+
Defined in: [packages/form-core/src/FieldApi.ts:1172](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1172)
197197

198198
Gets the current field value.
199199

@@ -213,7 +213,7 @@ Use `field.state.value` instead.
213213
handleBlur(): void
214214
```
215215

216-
Defined in: [packages/form-core/src/FieldApi.ts:1627](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1627)
216+
Defined in: [packages/form-core/src/FieldApi.ts:1632](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1632)
217217

218218
Handles the blur event.
219219

@@ -229,7 +229,7 @@ Handles the blur event.
229229
handleChange(updater): void
230230
```
231231

232-
Defined in: [packages/form-core/src/FieldApi.ts:1620](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1620)
232+
Defined in: [packages/form-core/src/FieldApi.ts:1625](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1625)
233233

234234
Handles the change event.
235235

@@ -254,7 +254,7 @@ insertValue(
254254
opts?): void
255255
```
256256
257-
Defined in: [packages/form-core/src/FieldApi.ts:1237](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1237)
257+
Defined in: [packages/form-core/src/FieldApi.ts:1242](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1242)
258258
259259
Inserts a value at the specified index, shifting the subsequent values to the right.
260260
@@ -307,7 +307,7 @@ moveValue(
307307
opts?): void
308308
```
309309
310-
Defined in: [packages/form-core/src/FieldApi.ts:1293](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1293)
310+
Defined in: [packages/form-core/src/FieldApi.ts:1298](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1298)
311311
312312
Moves the value at the first specified index to the second specified index.
313313
@@ -337,7 +337,7 @@ Moves the value at the first specified index to the second specified index.
337337
pushValue(value, opts?): void
338338
```
339339
340-
Defined in: [packages/form-core/src/FieldApi.ts:1222](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1222)
340+
Defined in: [packages/form-core/src/FieldApi.ts:1227](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1227)
341341
342342
Pushes a new value to the field.
343343
@@ -363,7 +363,7 @@ Pushes a new value to the field.
363363
removeValue(index, opts?): void
364364
```
365365
366-
Defined in: [packages/form-core/src/FieldApi.ts:1269](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1269)
366+
Defined in: [packages/form-core/src/FieldApi.ts:1274](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1274)
367367
368368
Removes a value at the specified index.
369369
@@ -392,7 +392,7 @@ replaceValue(
392392
opts?): void
393393
```
394394
395-
Defined in: [packages/form-core/src/FieldApi.ts:1253](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1253)
395+
Defined in: [packages/form-core/src/FieldApi.ts:1258](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1258)
396396
397397
Replaces a value at the specified index.
398398
@@ -422,7 +422,7 @@ Replaces a value at the specified index.
422422
setErrorMap(errorMap): void
423423
```
424424
425-
Defined in: [packages/form-core/src/FieldApi.ts:1647](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1647)
425+
Defined in: [packages/form-core/src/FieldApi.ts:1652](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1652)
426426
427427
Updates the field's errorMap
428428
@@ -444,7 +444,7 @@ Updates the field's errorMap
444444
setMeta(updater): void
445445
```
446446
447-
Defined in: [packages/form-core/src/FieldApi.ts:1190](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1190)
447+
Defined in: [packages/form-core/src/FieldApi.ts:1195](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1195)
448448
449449
Sets the field metadata.
450450
@@ -466,7 +466,7 @@ Sets the field metadata.
466466
setValue(updater, options?): void
467467
```
468468
469-
Defined in: [packages/form-core/src/FieldApi.ts:1174](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1174)
469+
Defined in: [packages/form-core/src/FieldApi.ts:1179](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1179)
470470
471471
Sets the field value and run the `change` validator.
472472
@@ -495,7 +495,7 @@ swapValues(
495495
opts?): void
496496
```
497497
498-
Defined in: [packages/form-core/src/FieldApi.ts:1281](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1281)
498+
Defined in: [packages/form-core/src/FieldApi.ts:1286](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1286)
499499
500500
Swaps the values at the specified indices.
501501
@@ -547,7 +547,7 @@ Updates the field instance with new options.
547547
validate(cause, opts?): unknown[] | Promise<unknown[]>
548548
```
549549
550-
Defined in: [packages/form-core/src/FieldApi.ts:1587](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1587)
550+
Defined in: [packages/form-core/src/FieldApi.ts:1592](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1592)
551551
552552
Validates the field value.
553553

packages/form-core/src/FieldApi.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,17 +1135,25 @@ export class FieldApi<
11351135
TParentSubmitMeta
11361136
>,
11371137
) => {
1138-
// Default Value
1138+
this.options = opts as never
11391139

1140+
const nameHasChanged = this.name !== opts.name
1141+
this.name = opts.name
1142+
1143+
// Default Value
11401144
if (this.state.value === undefined) {
11411145
const formDefault = getBy(opts.form.options.defaultValues, opts.name)
11421146

1143-
if (opts.defaultValue !== undefined) {
1144-
this.setValue(opts.defaultValue as never, {
1147+
const defaultValue = opts.defaultValue ?? formDefault
1148+
1149+
// The name is dynamic in array fields. It changes when the user performs operations like removing or reordering.
1150+
// In this case, we don't want to force a default value if the store managed to find an existing value.
1151+
if (nameHasChanged) {
1152+
this.setValue((val) => val || defaultValue, {
11451153
dontUpdateMeta: true,
11461154
})
1147-
} else if (formDefault !== undefined) {
1148-
this.setValue(formDefault as never, {
1155+
} else if (defaultValue !== undefined) {
1156+
this.setValue(defaultValue as never, {
11491157
dontUpdateMeta: true,
11501158
})
11511159
}
@@ -1155,9 +1163,6 @@ export class FieldApi<
11551163
if (this.form.getFieldMeta(this.name) === undefined) {
11561164
this.setMeta(this.state.meta)
11571165
}
1158-
1159-
this.options = opts as never
1160-
this.name = opts.name
11611166
}
11621167

11631168
/**

packages/react-form/tests/useField.test.tsx

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable react-compiler/react-compiler */
22
import { describe, expect, it, vi } from 'vitest'
3-
import { render, waitFor } from '@testing-library/react'
3+
import { render, waitFor, within } from '@testing-library/react'
44
import { userEvent } from '@testing-library/user-event'
55
import { StrictMode, useState } from 'react'
66
import { useStore } from '@tanstack/react-store'
@@ -1070,6 +1070,101 @@ describe('useField', () => {
10701070
expect(getByText(`["Test"]`)).toBeInTheDocument()
10711071
})
10721072

1073+
it('should handle removing element from array', async () => {
1074+
type Person = {
1075+
name: string
1076+
id: number
1077+
}
1078+
1079+
const fakePeople = {
1080+
jack: {
1081+
id: 5,
1082+
name: 'Jack',
1083+
},
1084+
molly: {
1085+
id: 6,
1086+
name: 'Molly',
1087+
},
1088+
george: {
1089+
id: 7,
1090+
name: 'George',
1091+
},
1092+
} satisfies Record<string, Person>
1093+
1094+
function Comp() {
1095+
const form = useForm({
1096+
defaultValues: {
1097+
people: [fakePeople.jack, fakePeople.molly, fakePeople.george],
1098+
},
1099+
})
1100+
1101+
return (
1102+
<form.Field name="people" mode="array">
1103+
{(field) => {
1104+
return (
1105+
<div>
1106+
<div data-testid="container">
1107+
{field.state.value.map((item, i) => {
1108+
return (
1109+
<form.Field key={item.id} name={`people[${i}].name`}>
1110+
{(subField) => {
1111+
return (
1112+
<div>
1113+
<label>
1114+
<div>Name for person {i}</div>
1115+
<span>{subField.state.value}</span>
1116+
<input
1117+
name={subField.name}
1118+
value={subField.state.value}
1119+
onChange={(e) =>
1120+
subField.handleChange(e.target.value)
1121+
}
1122+
/>
1123+
</label>
1124+
</div>
1125+
)
1126+
}}
1127+
</form.Field>
1128+
)
1129+
})}
1130+
</div>
1131+
<button onClick={() => field.removeValue(1)} type="button">
1132+
Remove person
1133+
</button>
1134+
</div>
1135+
)
1136+
}}
1137+
</form.Field>
1138+
)
1139+
}
1140+
1141+
const { getByText, queryByText, getByTestId } = render(
1142+
<StrictMode>
1143+
<Comp />
1144+
</StrictMode>,
1145+
)
1146+
1147+
let exisingPeople: Person[] = [
1148+
fakePeople.jack,
1149+
fakePeople.molly,
1150+
fakePeople.george,
1151+
]
1152+
exisingPeople.forEach((person) =>
1153+
expect(getByText(person.name)).toBeInTheDocument(),
1154+
)
1155+
const container = getByTestId('container')
1156+
expect(within(container).getAllByRole('textbox')).toHaveLength(3)
1157+
1158+
await user.click(getByText('Remove person'))
1159+
1160+
expect(within(container).getAllByRole('textbox')).toHaveLength(2)
1161+
exisingPeople = [fakePeople.jack, fakePeople.george]
1162+
exisingPeople.forEach((person) =>
1163+
expect(getByText(person.name)).toBeInTheDocument(),
1164+
)
1165+
expect(queryByText(fakePeople.molly.name)).not.toBeInTheDocument()
1166+
})
1167+
10731168
it('should not rerender unrelated fields', async () => {
10741169
const renderCount = {
10751170
field1: 0,

0 commit comments

Comments
 (0)