@tanstack/react-form 0.10.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/build/legacy/createFormFactory.cjs.map +1 -1
  2. package/build/legacy/createFormFactory.d.cts +5 -5
  3. package/build/legacy/createFormFactory.d.ts +5 -5
  4. package/build/legacy/createFormFactory.js.map +1 -1
  5. package/build/legacy/formContext.cjs.map +1 -1
  6. package/build/legacy/formContext.d.cts +3 -3
  7. package/build/legacy/formContext.d.ts +3 -3
  8. package/build/legacy/formContext.js.map +1 -1
  9. package/build/legacy/types.cjs.map +1 -1
  10. package/build/legacy/types.d.cts +2 -2
  11. package/build/legacy/types.d.ts +2 -2
  12. package/build/legacy/useField.cjs.map +1 -1
  13. package/build/legacy/useField.d.cts +13 -13
  14. package/build/legacy/useField.d.ts +13 -13
  15. package/build/legacy/useField.js.map +1 -1
  16. package/build/legacy/useForm.cjs.map +1 -1
  17. package/build/legacy/useForm.d.cts +7 -9
  18. package/build/legacy/useForm.d.ts +7 -9
  19. package/build/legacy/useForm.js.map +1 -1
  20. package/build/modern/createFormFactory.cjs.map +1 -1
  21. package/build/modern/createFormFactory.d.cts +5 -5
  22. package/build/modern/createFormFactory.d.ts +5 -5
  23. package/build/modern/createFormFactory.js.map +1 -1
  24. package/build/modern/formContext.cjs.map +1 -1
  25. package/build/modern/formContext.d.cts +3 -3
  26. package/build/modern/formContext.d.ts +3 -3
  27. package/build/modern/formContext.js.map +1 -1
  28. package/build/modern/types.cjs.map +1 -1
  29. package/build/modern/types.d.cts +2 -2
  30. package/build/modern/types.d.ts +2 -2
  31. package/build/modern/useField.cjs.map +1 -1
  32. package/build/modern/useField.d.cts +13 -13
  33. package/build/modern/useField.d.ts +13 -13
  34. package/build/modern/useField.js.map +1 -1
  35. package/build/modern/useForm.cjs.map +1 -1
  36. package/build/modern/useForm.d.cts +7 -9
  37. package/build/modern/useForm.d.ts +7 -9
  38. package/build/modern/useForm.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/createFormFactory.ts +15 -9
  41. package/src/formContext.ts +2 -2
  42. package/src/tests/createFormFactory.test.tsx +1 -1
  43. package/src/tests/useField.test-d.tsx +10 -6
  44. package/src/tests/useField.test.tsx +45 -33
  45. package/src/tests/useForm.test-d.tsx +36 -0
  46. package/src/tests/useForm.test.tsx +57 -39
  47. package/src/types.ts +13 -4
  48. package/src/useField.tsx +67 -37
  49. package/src/useForm.tsx +16 -11
@@ -15,7 +15,7 @@ describe('useForm', () => {
15
15
  lastName: string
16
16
  }
17
17
 
18
- const formFactory = createFormFactory<Person, unknown>()
18
+ const formFactory = createFormFactory<Person>()
19
19
 
20
20
  function Comp() {
21
21
  const form = formFactory.useForm()
@@ -53,7 +53,7 @@ describe('useForm', () => {
53
53
  lastName: string
54
54
  }
55
55
 
56
- const formFactory = createFormFactory<Person, unknown>()
56
+ const formFactory = createFormFactory<Person>()
57
57
 
58
58
  function Comp() {
59
59
  const form = formFactory.useForm({
@@ -90,8 +90,8 @@ describe('useForm', () => {
90
90
  defaultValues: {
91
91
  firstName: 'FirstName',
92
92
  },
93
- onSubmit: (data) => {
94
- setSubmittedData(data)
93
+ onSubmit: ({ value }) => {
94
+ setSubmittedData(value)
95
95
  },
96
96
  })
97
97
 
@@ -135,9 +135,11 @@ describe('useForm', () => {
135
135
  defaultValues: {
136
136
  firstName: 'FirstName',
137
137
  },
138
- onMount: () => {
139
- setFormMounted(true)
140
- return undefined
138
+ validators: {
139
+ onMount: () => {
140
+ setFormMounted(true)
141
+ return undefined
142
+ },
141
143
  },
142
144
  })
143
145
 
@@ -166,12 +168,14 @@ describe('useForm', () => {
166
168
  }
167
169
  const error = 'Please enter a different value'
168
170
 
169
- const formFactory = createFormFactory<Person, unknown>()
171
+ const formFactory = createFormFactory<Person>()
170
172
 
171
173
  function Comp() {
172
174
  const form = formFactory.useForm({
173
- onChange() {
174
- return error
175
+ validators: {
176
+ onChange() {
177
+ return error
178
+ },
175
179
  },
176
180
  })
177
181
  return (
@@ -210,11 +214,14 @@ describe('useForm', () => {
210
214
  }
211
215
  const error = 'Please enter a different value'
212
216
 
213
- const formFactory = createFormFactory<Person, unknown>()
217
+ const formFactory = createFormFactory<Person>()
214
218
 
215
219
  function Comp() {
216
220
  const form = formFactory.useForm({
217
- onChange: (value) => (value.firstName === 'other' ? error : undefined),
221
+ validators: {
222
+ onChange: ({ value }) =>
223
+ value.firstName === 'other' ? error : undefined,
224
+ },
218
225
  })
219
226
 
220
227
  const errors = form.useStore((s) => s.errors)
@@ -252,11 +259,14 @@ describe('useForm', () => {
252
259
  }
253
260
  const error = 'Please enter a different value'
254
261
 
255
- const formFactory = createFormFactory<Person, unknown>()
262
+ const formFactory = createFormFactory<Person>()
256
263
 
257
264
  function Comp() {
258
265
  const form = formFactory.useForm({
259
- onChange: (value) => (value.firstName === 'other' ? error : undefined),
266
+ validators: {
267
+ onChange: ({ value }) =>
268
+ value.firstName === 'other' ? error : undefined,
269
+ },
260
270
  })
261
271
  const errors = form.useStore((s) => s.errorMap)
262
272
  return (
@@ -297,13 +307,15 @@ describe('useForm', () => {
297
307
  defaultValues: {
298
308
  firstName: '',
299
309
  },
300
- onChange: (vals) => {
301
- if (vals.firstName === 'other') return onChangeError
302
- return undefined
303
- },
304
- onBlur: (vals) => {
305
- if (vals.firstName === 'other') return onBlurError
306
- return undefined
310
+ validators: {
311
+ onChange: ({ value }) => {
312
+ if (value.firstName === 'other') return onChangeError
313
+ return undefined
314
+ },
315
+ onBlur: ({ value }) => {
316
+ if (value.firstName === 'other') return onBlurError
317
+ return undefined
318
+ },
307
319
  },
308
320
  })
309
321
 
@@ -347,13 +359,15 @@ describe('useForm', () => {
347
359
  }
348
360
  const error = 'Please enter a different value'
349
361
 
350
- const formFactory = createFormFactory<Person, unknown>()
362
+ const formFactory = createFormFactory<Person>()
351
363
 
352
364
  function Comp() {
353
365
  const form = formFactory.useForm({
354
- onChangeAsync: async () => {
355
- await sleep(10)
356
- return error
366
+ validators: {
367
+ onChangeAsync: async () => {
368
+ await sleep(10)
369
+ return error
370
+ },
357
371
  },
358
372
  })
359
373
  const errors = form.useStore((s) => s.errorMap)
@@ -395,17 +409,19 @@ describe('useForm', () => {
395
409
  const onChangeError = 'Please enter a different value (onChangeError)'
396
410
  const onBlurError = 'Please enter a different value (onBlurError)'
397
411
 
398
- const formFactory = createFormFactory<Person, unknown>()
412
+ const formFactory = createFormFactory<Person>()
399
413
 
400
414
  function Comp() {
401
415
  const form = formFactory.useForm({
402
- onChangeAsync: async () => {
403
- await sleep(10)
404
- return onChangeError
405
- },
406
- onBlurAsync: async () => {
407
- await sleep(10)
408
- return onBlurError
416
+ validators: {
417
+ onChangeAsync: async () => {
418
+ await sleep(10)
419
+ return onChangeError
420
+ },
421
+ onBlurAsync: async () => {
422
+ await sleep(10)
423
+ return onBlurError
424
+ },
409
425
  },
410
426
  })
411
427
  const errors = form.useStore((s) => s.errorMap)
@@ -453,15 +469,17 @@ describe('useForm', () => {
453
469
  }
454
470
  const mockFn = vi.fn()
455
471
  const error = 'Please enter a different value'
456
- const formFactory = createFormFactory<Person, unknown>()
472
+ const formFactory = createFormFactory<Person>()
457
473
 
458
474
  function Comp() {
459
475
  const form = formFactory.useForm({
460
- onChangeAsyncDebounceMs: 100,
461
- onChangeAsync: async () => {
462
- mockFn()
463
- await sleep(10)
464
- return error
476
+ validators: {
477
+ onChangeAsyncDebounceMs: 100,
478
+ onChangeAsync: async () => {
479
+ mockFn()
480
+ await sleep(10)
481
+ return error
482
+ },
465
483
  },
466
484
  })
467
485
  const errors = form.useStore((s) => s.errors)
package/src/types.ts CHANGED
@@ -1,11 +1,20 @@
1
- import type { FieldOptions, DeepKeys, DeepValue } from '@tanstack/form-core'
1
+ import type {
2
+ FieldOptions,
3
+ DeepKeys,
4
+ DeepValue,
5
+ Validator,
6
+ } from '@tanstack/form-core'
2
7
 
3
8
  export type UseFieldOptions<
4
9
  TParentData,
5
10
  TName extends DeepKeys<TParentData>,
6
- ValidatorType,
7
- FormValidator,
11
+ TFieldValidator extends
12
+ | Validator<DeepValue<TParentData, TName>, unknown>
13
+ | undefined = undefined,
14
+ TFormValidator extends
15
+ | Validator<TParentData, unknown>
16
+ | undefined = undefined,
8
17
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
9
- > = FieldOptions<TParentData, TName, ValidatorType, FormValidator, TData> & {
18
+ > = FieldOptions<TParentData, TName, TFieldValidator, TFormValidator, TData> & {
10
19
  mode?: 'value' | 'array'
11
20
  }
package/src/useField.tsx CHANGED
@@ -1,6 +1,11 @@
1
1
  import React, { useState } from 'react'
2
2
  import { useStore } from '@tanstack/react-store'
3
- import type { DeepKeys, DeepValue, Narrow } from '@tanstack/form-core'
3
+ import type {
4
+ DeepKeys,
5
+ DeepValue,
6
+ Narrow,
7
+ Validator,
8
+ } from '@tanstack/form-core'
4
9
  import { FieldApi, functionalUpdate } from '@tanstack/form-core'
5
10
  import { useFormContext, formContext } from './formContext'
6
11
  import type { UseFieldOptions } from './types'
@@ -11,49 +16,53 @@ declare module '@tanstack/form-core' {
11
16
  interface FieldApi<
12
17
  TParentData,
13
18
  TName extends DeepKeys<TParentData>,
14
- ValidatorType,
15
- FormValidator,
19
+ TFieldValidator extends
20
+ | Validator<DeepValue<TParentData, TName>, unknown>
21
+ | undefined = undefined,
22
+ TFormValidator extends
23
+ | Validator<TParentData, unknown>
24
+ | undefined = undefined,
16
25
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
17
26
  > {
18
- Field: FieldComponent<TData, FormValidator>
27
+ Field: FieldComponent<TParentData, TFormValidator>
19
28
  }
20
29
  }
21
30
 
22
31
  export type UseField<TParentData> = <
23
32
  TName extends DeepKeys<TParentData>,
24
- ValidatorType,
25
- FormValidator,
33
+ TFieldValidator extends
34
+ | Validator<DeepValue<TParentData, TName>, unknown>
35
+ | undefined = undefined,
36
+ TFormValidator extends
37
+ | Validator<TParentData, unknown>
38
+ | undefined = undefined,
26
39
  >(
27
40
  opts?: { name: Narrow<TName> } & UseFieldOptions<
28
41
  TParentData,
29
42
  TName,
30
- ValidatorType,
31
- FormValidator
43
+ TFieldValidator,
44
+ TFormValidator
32
45
  >,
33
46
  ) => FieldApi<
34
47
  TParentData,
35
48
  TName,
36
- ValidatorType,
37
- FormValidator,
49
+ TFieldValidator,
50
+ TFormValidator,
38
51
  DeepValue<TParentData, TName>
39
52
  >
40
53
 
41
54
  export function useField<
42
55
  TParentData,
43
56
  TName extends DeepKeys<TParentData>,
44
- ValidatorType,
45
- FormValidator,
57
+ TFieldValidator extends
58
+ | Validator<DeepValue<TParentData, TName>, unknown>
59
+ | undefined = undefined,
60
+ TFormValidator extends
61
+ | Validator<TParentData, unknown>
62
+ | undefined = undefined,
46
63
  >(
47
- opts: UseFieldOptions<TParentData, TName, ValidatorType, FormValidator>,
48
- ): FieldApi<
49
- TParentData,
50
- TName,
51
- ValidatorType,
52
- FormValidator
53
- // Omit<typeof opts, 'onMount'> & {
54
- // form: FormApi<TParentData>
55
- // }
56
- > {
64
+ opts: UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,
65
+ ): FieldApi<TParentData, TName, TFieldValidator, TFormValidator> {
57
66
  // Get the form API either manually or from context
58
67
  const { formApi, parentFieldName } = useFormContext()
59
68
 
@@ -68,9 +77,9 @@ export function useField<
68
77
 
69
78
  const api = new FieldApi({
70
79
  ...opts,
71
- form: formApi,
80
+ form: formApi as never,
72
81
  // TODO: Fix typings to include `index` and `parentFieldName`, if present
73
- name: name as typeof opts.name,
82
+ name: name as typeof opts.name as never,
74
83
  })
75
84
 
76
85
  api.Field = Field as never
@@ -97,18 +106,28 @@ export function useField<
97
106
  // Instantiates field meta and removes it when unrendered
98
107
  useIsomorphicLayoutEffect(() => fieldApi.mount(), [fieldApi])
99
108
 
100
- return fieldApi
109
+ return fieldApi as never
101
110
  }
102
111
 
103
112
  type FieldComponentProps<
104
113
  TParentData,
105
114
  TName extends DeepKeys<TParentData>,
106
- ValidatorType,
107
- FormValidator,
115
+ TFieldValidator extends
116
+ | Validator<DeepValue<TParentData, TName>, unknown>
117
+ | undefined = undefined,
118
+ TFormValidator extends
119
+ | Validator<TParentData, unknown>
120
+ | undefined = undefined,
108
121
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
109
122
  > = {
110
123
  children: (
111
- fieldApi: FieldApi<TParentData, TName, ValidatorType, FormValidator, TData>,
124
+ fieldApi: FieldApi<
125
+ TParentData,
126
+ TName,
127
+ TFieldValidator,
128
+ TFormValidator,
129
+ TData
130
+ >,
112
131
  ) => any
113
132
  } & (TParentData extends any[]
114
133
  ? {
@@ -120,13 +139,20 @@ type FieldComponentProps<
120
139
  index?: never
121
140
  }) &
122
141
  Omit<
123
- UseFieldOptions<TParentData, TName, ValidatorType, FormValidator>,
142
+ UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>,
124
143
  'name' | 'index'
125
144
  >
126
145
 
127
- export type FieldComponent<TParentData, FormValidator> = <
146
+ export type FieldComponent<
147
+ TParentData,
148
+ TFormValidator extends
149
+ | Validator<TParentData, unknown>
150
+ | undefined = undefined,
151
+ > = <
128
152
  TName extends DeepKeys<TParentData>,
129
- ValidatorType,
153
+ TFieldValidator extends
154
+ | Validator<DeepValue<TParentData, TName>, unknown>
155
+ | undefined = undefined,
130
156
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
131
157
  >({
132
158
  children,
@@ -134,24 +160,28 @@ export type FieldComponent<TParentData, FormValidator> = <
134
160
  }: FieldComponentProps<
135
161
  TParentData,
136
162
  TName,
137
- ValidatorType,
138
- FormValidator,
163
+ TFieldValidator,
164
+ TFormValidator,
139
165
  TData
140
166
  >) => any
141
167
 
142
168
  export function Field<
143
169
  TParentData,
144
170
  TName extends DeepKeys<TParentData>,
145
- ValidatorType,
146
- FormValidator,
171
+ TFieldValidator extends
172
+ | Validator<DeepValue<TParentData, TName>, unknown>
173
+ | undefined = undefined,
174
+ TFormValidator extends
175
+ | Validator<TParentData, unknown>
176
+ | undefined = undefined,
147
177
  >({
148
178
  children,
149
179
  ...fieldOptions
150
180
  }: {
151
181
  children: (
152
- fieldApi: FieldApi<TParentData, TName, ValidatorType, FormValidator>,
182
+ fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator>,
153
183
  ) => any
154
- } & UseFieldOptions<TParentData, TName, ValidatorType, FormValidator>) {
184
+ } & UseFieldOptions<TParentData, TName, TFieldValidator, TFormValidator>) {
155
185
  const fieldApi = useField(fieldOptions as any)
156
186
 
157
187
  return (
package/src/useForm.tsx CHANGED
@@ -1,17 +1,17 @@
1
- import type { FormState, FormOptions } from '@tanstack/form-core'
1
+ import type { FormState, FormOptions, Validator } from '@tanstack/form-core'
2
2
  import { FormApi, functionalUpdate } from '@tanstack/form-core'
3
3
  import type { NoInfer } from '@tanstack/react-store'
4
4
  import { useStore } from '@tanstack/react-store'
5
- import React, { type ReactNode, useState } from 'react'
5
+ import React, { type PropsWithChildren, type ReactNode, useState } from 'react'
6
6
  import { type UseField, type FieldComponent, Field, useField } from './useField'
7
7
  import { formContext } from './formContext'
8
8
  import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
9
9
 
10
10
  declare module '@tanstack/form-core' {
11
11
  // eslint-disable-next-line no-shadow
12
- interface FormApi<TFormData, ValidatorType> {
13
- Provider: (props: { children: any }) => any
14
- Field: FieldComponent<TFormData, ValidatorType>
12
+ interface FormApi<TFormData, TFormValidator> {
13
+ Provider: (props: PropsWithChildren) => JSX.Element
14
+ Field: FieldComponent<TFormData, TFormValidator>
15
15
  useField: UseField<TFormData>
16
16
  useStore: <TSelected = NoInfer<FormState<TFormData>>>(
17
17
  selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
@@ -19,20 +19,25 @@ declare module '@tanstack/form-core' {
19
19
  Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
20
20
  selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
21
21
  children: ((state: NoInfer<TSelected>) => ReactNode) | ReactNode
22
- }) => any
22
+ }) => JSX.Element
23
23
  }
24
24
  }
25
25
 
26
- export function useForm<TData, FormValidator>(
27
- opts?: FormOptions<TData, FormValidator>,
28
- ): FormApi<TData, FormValidator> {
26
+ export function useForm<
27
+ TFormData,
28
+ TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,
29
+ >(
30
+ opts?: FormOptions<TFormData, TFormValidator>,
31
+ ): FormApi<TFormData, TFormValidator> {
29
32
  const [formApi] = useState(() => {
30
33
  // @ts-ignore
31
- const api = new FormApi<TData>(opts)
34
+ const api = new FormApi<TFormData, TFormValidator>(opts)
32
35
 
33
36
  api.Provider = function Provider(props) {
34
37
  useIsomorphicLayoutEffect(api.mount, [])
35
- return <formContext.Provider {...props} value={{ formApi: api }} />
38
+ return (
39
+ <formContext.Provider {...props} value={{ formApi: api as never }} />
40
+ )
36
41
  }
37
42
  api.Field = Field as any
38
43
  api.useField = useField as any