Skip to content

Commit 6d75eba

Browse files
committed
fix: update form props on render
1 parent 30e7433 commit 6d75eba

File tree

3 files changed

+223
-1
lines changed

3 files changed

+223
-1
lines changed

docs/guides/basic-concepts.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
id: basic-concepts
3+
title: Basic Concepts and Terminology
4+
---
5+
6+
# Basic Concepts and Terminology
7+
8+
This page introduces the basic concepts and terminology used in the @tanstack/react-form library. Familiarizing yourself with these concepts will help you better understand and work with the library.
9+
10+
## Form Factory
11+
12+
The Form Factory is responsible for creating form instances with a shared configuration. It is created using the `createFormFactory` function, which accepts a configuration object with default values for the form fields. This shared configuration allows you to create multiple form instances with consistent behavior.
13+
14+
Example:
15+
16+
```tsx
17+
const formFactory = createFormFactory<Person>({
18+
defaultValues: {
19+
firstName: '',
20+
lastName: '',
21+
hobbies: [],
22+
},
23+
})
24+
```
25+
26+
## Form Instance
27+
28+
A Form Instance is an object that represents an individual form and provides methods and properties for working with the form. You create a form instance using the useForm hook provided by the form factory. The hook accepts an object with an onSubmit function, which is called when the form is submitted.
29+
30+
```tsx
31+
const form = formFactory.useForm({
32+
onSubmit: async (values) => {
33+
// Do something with form data
34+
console.log(values)
35+
},
36+
})
37+
```
38+
39+
## Field
40+
41+
A Field represents a single form input element, such as a text input or a checkbox. Fields are created using the form.Field component provided by the form instance. The component accepts a name prop, which should match a key in the form's default values. It also accepts a children prop, which is a render prop function that takes a field object as its argument.
42+
43+
Example:
44+
45+
```tsx
46+
<form.Field
47+
name="firstName"
48+
children={(field) => (
49+
<>
50+
<input {...field.getInputProps()} />
51+
<FieldInfo field={field} />
52+
</>
53+
)}
54+
/>
55+
```
56+
57+
## Field State
58+
59+
Each field has its own state, which includes its current value, validation status, error messages, and other metadata. You can access a field's state using the field.state property.
60+
61+
Example:
62+
63+
```tsx
64+
const { value, error, touched, isValidating } = field.state
65+
```
66+
67+
## Field API
68+
69+
The Field API is an object passed to the render prop function when creating a field. It provides methods for working with the field's state, such as getInputProps, which returns an object with props needed to bind the field to a form input element.
70+
71+
Example:
72+
73+
```tsx
74+
<input {...field.getInputProps()} />
75+
```
76+
77+
## Validation
78+
79+
@tanstack/react-form provides both synchronous and asynchronous validation out of the box. Validation functions can be passed to the form.Field component using the validate and validateAsync props.
80+
81+
Example:
82+
83+
```tsx
84+
<form.Field
85+
name="firstName"
86+
validate={(value) => !value && 'A first name is required'}
87+
validateAsync={async (value) => {
88+
await new Promise((resolve) => setTimeout(resolve, 1000))
89+
return value.includes('error') && 'No "error" allowed in first name'
90+
}}
91+
children={(field) => (
92+
<>
93+
<input {...field.getInputProps()} />
94+
<FieldInfo field={field} />
95+
</>
96+
)}
97+
/>
98+
```
99+
100+
## Reactivity
101+
102+
@tanstack/react-form offers various ways to subscribe to form and field state changes, such as the form.useStore hook, the form.Subscribe component, and the form.useField hook. These methods allow you to optimize your form's rendering performance by only updating components when necessary.
103+
104+
Example:
105+
106+
```tsx
107+
<form.Subscribe
108+
selector={(state) => [state.canSubmit, state.isSubmitting]}
109+
children={([canSubmit, isSubmitting]) => (
110+
<button type="submit" disabled={!canSubmit}>
111+
{isSubmitting ? '...' : 'Submit'}
112+
</button>
113+
)}
114+
/>
115+
```
116+
117+
## Array Fields
118+
119+
Array fields allow you to manage a list of values within a form, such as a list of hobbies. You can create an array field using the form.Field component with the mode="array" prop. The component accepts a children prop, which is a render prop function that takes an arrayField object as its argument.
120+
121+
When working with array fields, you can use the fields `pushValue`, `removeValue`, and `swapValues` methods to add, remove, and swap values in the array.
122+
123+
Example:
124+
125+
```tsx
126+
<form.Field
127+
name="hobbies"
128+
mode="array"
129+
children={(hobbiesField) => (
130+
<div>
131+
Hobbies
132+
<div>
133+
{!hobbiesField.state.value.length
134+
? 'No hobbies found.'
135+
: hobbiesField.state.value.map((_, i) => (
136+
<div key={i}>
137+
<hobbiesField.Field
138+
index={i}
139+
name="name"
140+
children={(field) => {
141+
return (
142+
<div>
143+
<label htmlFor={field.name}>Name:</label>
144+
<input name={field.name} {...field.getInputProps()} />
145+
<button
146+
type="button"
147+
onClick={() => hobbiesField.removeValue(i)}
148+
>
149+
X
150+
</button>
151+
<FieldInfo field={field} />
152+
</div>
153+
)
154+
}}
155+
/>
156+
<hobbiesField.Field
157+
index={i}
158+
name="description"
159+
children={(field) => {
160+
return (
161+
<div>
162+
<label htmlFor={field.name}>Description:</label>
163+
<input name={field.name} {...field.getInputProps()} />
164+
<FieldInfo field={field} />
165+
</div>
166+
)
167+
}}
168+
/>
169+
</div>
170+
))}
171+
</div>
172+
<button
173+
type="button"
174+
onClick={() =>
175+
hobbiesField.pushValue({
176+
name: '',
177+
description: '',
178+
yearsOfExperience: 0,
179+
})
180+
}
181+
>
182+
Add hobby
183+
</button>
184+
</div>
185+
)}
186+
/>
187+
```
188+
189+
## Array-Nested Fields
190+
191+
Rendering fields that are items of a nested array require only a small change to the `form.Field` component props.
192+
193+
Example:
194+
195+
```tsx
196+
<hobbiesField.Field
197+
index={i}
198+
name="name"
199+
children={(field) => {
200+
return (
201+
<div>
202+
<label htmlFor={field.name}>Name:</label>
203+
<input name={field.name} {...field.getInputProps()} />
204+
<button type="button" onClick={() => hobbiesField.removeValue(i)}>
205+
X
206+
</button>
207+
<FieldInfo field={field} />
208+
</div>
209+
)
210+
}}
211+
/>
212+
```
213+
214+
These are the basic concepts and terminology used in the @tanstack/react-form library. Understanding these concepts will help you work more effectively with the library and create complex forms with ease.
215+
216+
```
217+
218+
```

packages/form-core/src/FormApi.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ export class FormApi<TFormData> {
148148
this.update(opts || {})
149149
}
150150

151-
update = (options: FormOptions<TFormData>) => {
151+
update = (options?: FormOptions<TFormData>) => {
152+
if (!options) return
153+
152154
this.store.batch(() => {
153155
if (
154156
options.defaultState &&

packages/react-form/src/useForm.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export function useForm<TData>(opts?: FormOptions<TData>): FormApi<TData> {
5757
return api
5858
})
5959

60+
formApi.update(opts)
61+
6062
return formApi as any
6163
}
6264

0 commit comments

Comments
 (0)