Skip to content

Commit e04526a

Browse files
committed
fix(form-core): move setting default value to mount function to avoid react set state on render error + include test in react package
1 parent 135b886 commit e04526a

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

packages/form-core/src/FieldApi.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -996,11 +996,6 @@ export class FieldApi<
996996
this.form = opts.form as never
997997
this.name = opts.name as never
998998
this.timeoutIds = {} as Record<ValidationCause, never>
999-
if (opts.defaultValue !== undefined) {
1000-
this.form.setFieldValue(this.name, opts.defaultValue as never, {
1001-
dontUpdateMeta: true,
1002-
})
1003-
}
1004999

10051000
this.store = new Derived({
10061001
deps: [this.form.store],
@@ -1077,6 +1072,12 @@ export class FieldApi<
10771072
mount = () => {
10781073
const cleanup = this.store.mount()
10791074

1075+
if (this.options.defaultValue !== undefined) {
1076+
this.form.setFieldValue(this.name, this.options.defaultValue as never, {
1077+
dontUpdateMeta: true,
1078+
})
1079+
}
1080+
10801081
const info = this.getInfo()
10811082
info.instance = this as never
10821083

@@ -1519,7 +1520,6 @@ export class FieldApi<
15191520

15201521
// TODO: Dedupe this logic to reduce bundle size
15211522
for (const validateObj of validates) {
1522-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
15231523
if (!validateObj.validate) continue
15241524
validateFieldAsyncFn(this, validateObj, validatesPromises)
15251525
}

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

+47
Original file line numberDiff line numberDiff line change
@@ -1130,4 +1130,51 @@ describe('useField', () => {
11301130
// field2 should not have rerendered
11311131
expect(renderCount.field2).toBe(field2InitialRender)
11321132
})
1133+
1134+
it('should handle defaultValue without setstate-in-render error', async () => {
1135+
// Spy on console.error before rendering
1136+
const consoleErrorSpy = vi.spyOn(console, 'error')
1137+
1138+
function Comp() {
1139+
const form = useForm({
1140+
defaultValues: {
1141+
fieldOne: '',
1142+
fieldTwo: '',
1143+
},
1144+
})
1145+
1146+
const fieldOne = useStore(form.store, (state) => state.values.fieldOne)
1147+
1148+
return (
1149+
<form>
1150+
<form.Field
1151+
name="fieldOne"
1152+
children={(field) => {
1153+
return (
1154+
<input
1155+
data-testid={field.name}
1156+
id={field.name}
1157+
value={field.state.value}
1158+
onChange={(e) => field.handleChange(e.target.value)}
1159+
/>
1160+
)
1161+
}}
1162+
/>
1163+
{fieldOne && (
1164+
<form.Field
1165+
name="fieldTwo"
1166+
defaultValue="default field two value"
1167+
children={(_) => null}
1168+
/>
1169+
)}
1170+
</form>
1171+
)
1172+
}
1173+
1174+
const { getByTestId } = render(<Comp />)
1175+
await user.type(getByTestId('fieldOne'), 'John')
1176+
1177+
// Should not log an error
1178+
expect(consoleErrorSpy).not.toHaveBeenCalled()
1179+
})
11331180
})

0 commit comments

Comments
 (0)