-
-
Notifications
You must be signed in to change notification settings - Fork 345
/
index.tsx
129 lines (122 loc) · 3.67 KB
/
index.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import * as React from 'react'
import { createRoot } from 'react-dom/client'
import { useForm } from '@tanstack/react-form'
import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'
import type { FieldApi } from '@tanstack/react-form'
function FieldInfo({ field }: { field: FieldApi<any, any, any, any> }) {
return (
<>
{field.state.meta.isTouched && field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(',')}</em>
) : null}
{field.state.meta.isValidating ? 'Validating...' : null}
</>
)
}
const userSchema = z.object({
firstName: z.string().refine((val) => val !== 'John', {
message: '[Form] First name cannot be John',
}),
lastName: z.string().min(3, '[Form] Last name must be at least 3 characters'),
})
type User = z.infer<typeof userSchema>
export default function App() {
const form = useForm({
defaultValues: {
firstName: '',
lastName: '',
} as User,
onSubmit: async ({ value }) => {
// Do something with form data
console.log(value)
},
// Add a validator to support Zod usage in Form and Field
validatorAdapter: zodValidator(),
validators: {
onChange: userSchema,
},
})
return (
<div>
<h1>Zod Form Example</h1>
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<div>
{/* A type-safe field component*/}
<form.Field
name="firstName"
validators={{
onChange: z
.string()
.min(3, '[Field] First name must be at least 3 characters'),
onChangeAsyncDebounceMs: 500,
onChangeAsync: z.string().refine(
async (value) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
return !value.includes('error')
},
{
message: "[Field] No 'error' allowed in first name",
},
),
}}
children={(field) => {
// Avoid hasty abstractions. Render props are great!
return (
<>
<label htmlFor={field.name}>First Name:</label>
<input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
<FieldInfo field={field} />
</>
)
}}
/>
</div>
<div>
<form.Field
name="lastName"
children={(field) => (
<>
<label htmlFor={field.name}>Last Name:</label>
<input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
<FieldInfo field={field} />
</>
)}
/>
</div>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? '...' : 'Submit'}
</button>
)}
/>
</form>
</div>
)
}
const rootElement = document.getElementById('root')!
createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)