0
# Hooks API
1
2
Modern React hooks for accessing form context, field state, and form state with customizable subscriptions and full TypeScript support.
3
4
## Capabilities
5
6
### useForm Hook
7
8
Hook to access the form API from within form components and field components.
9
10
```typescript { .api }
11
/**
12
* Hook to access form API from React context
13
* @param componentName - Optional component name for error messages
14
* @returns Form API instance
15
* @throws Error if used outside of Form component
16
*/
17
function useForm<FormValues = Record<string, any>>(
18
componentName?: string
19
): FormApi<FormValues>;
20
```
21
22
**Usage Examples:**
23
24
```typescript
25
import React from "react";
26
import { Form, useForm } from "react-final-form";
27
28
// Basic useForm usage
29
function CustomFormButton() {
30
const form = useForm();
31
32
return (
33
<button
34
type="button"
35
onClick={() => form.reset()}
36
>
37
Reset Form
38
</button>
39
);
40
}
41
42
// useForm with component name for better error messages
43
function CustomField() {
44
const form = useForm("CustomField");
45
46
const handleSetValue = () => {
47
form.change("myField", "new value");
48
};
49
50
return (
51
<div>
52
<button onClick={handleSetValue}>Set Value</button>
53
<button onClick={() => form.focus("myField")}>Focus Field</button>
54
</div>
55
);
56
}
57
58
// Using form API for programmatic control
59
function FormControls() {
60
const form = useForm();
61
62
const handleBatch = () => {
63
form.batch(() => {
64
form.change("firstName", "John");
65
form.change("lastName", "Doe");
66
form.change("email", "john.doe@example.com");
67
});
68
};
69
70
return (
71
<div>
72
<button onClick={handleBatch}>Fill Form</button>
73
<button onClick={() => form.restart()}>Restart</button>
74
<button onClick={() => form.reset()}>Reset</button>
75
</div>
76
);
77
}
78
79
// Complete form example with useForm
80
function MyForm() {
81
return (
82
<Form onSubmit={(values) => console.log(values)}>
83
{({ handleSubmit }) => (
84
<form onSubmit={handleSubmit}>
85
<input name="firstName" />
86
<CustomFormButton />
87
</form>
88
)}
89
</Form>
90
);
91
}
92
```
93
94
### useField Hook
95
96
Hook for field-level state management that returns field input props and metadata.
97
98
```typescript { .api }
99
/**
100
* Hook for field state management and input props
101
* @param name - Field name (required)
102
* @param config - Field configuration options
103
* @returns Field render props with input and meta
104
*/
105
function useField<
106
FieldValue = any,
107
T extends HTMLElement = HTMLElement,
108
FormValues = Record<string, any>
109
>(
110
name: string,
111
config?: UseFieldConfig
112
): FieldRenderProps<FieldValue, T, FormValues>;
113
114
interface UseFieldConfig extends UseFieldAutoConfig {
115
/** Field state subscription configuration */
116
subscription?: FieldSubscription;
117
}
118
119
interface FieldRenderProps<
120
FieldValue = any,
121
T extends HTMLElement = HTMLElement,
122
FormValues = any
123
> {
124
/** Input props to spread on form elements */
125
input: FieldInputProps<FieldValue, T>;
126
/** Field metadata and state information */
127
meta: FieldMeta;
128
}
129
```
130
131
**Usage Examples:**
132
133
```typescript
134
import React from "react";
135
import { Form, useField } from "react-final-form";
136
137
// Basic useField usage
138
function CustomTextField({ name, ...props }: { name: string }) {
139
const { input, meta } = useField(name);
140
141
return (
142
<div>
143
<input {...input} {...props} />
144
{meta.error && meta.touched && <span>{meta.error}</span>}
145
</div>
146
);
147
}
148
149
// useField with validation
150
function ValidatedInput({ name, validate, ...props }: any) {
151
const { input, meta } = useField(name, { validate });
152
153
return (
154
<div className={meta.error ? "error" : ""}>
155
<input {...input} {...props} />
156
{meta.error && meta.touched && (
157
<div className="error-message">{meta.error}</div>
158
)}
159
{meta.validating && <div className="validating">Validating...</div>}
160
</div>
161
);
162
}
163
164
// useField with formatting and parsing
165
function CurrencyInput({ name }: { name: string }) {
166
const format = (value: number) => {
167
if (value === undefined) return "";
168
return `$${value.toFixed(2)}`;
169
};
170
171
const parse = (value: string) => {
172
const number = parseFloat(value.replace(/[^0-9.-]/g, ""));
173
return isNaN(number) ? undefined : number;
174
};
175
176
const { input, meta } = useField(name, { format, parse });
177
178
return (
179
<div>
180
<input {...input} type="text" placeholder="$0.00" />
181
{meta.error && meta.touched && <span>{meta.error}</span>}
182
</div>
183
);
184
}
185
186
// useField with subscription optimization
187
function OptimizedField({ name }: { name: string }) {
188
const { input, meta } = useField(name, {
189
subscription: { value: true, error: true, touched: true }
190
});
191
192
return (
193
<div>
194
<input {...input} />
195
{meta.error && meta.touched && <span>{meta.error}</span>}
196
</div>
197
);
198
}
199
200
// Custom checkbox component
201
function CustomCheckbox({ name, label }: { name: string; label: string }) {
202
const { input, meta } = useField(name, { type: "checkbox" });
203
204
return (
205
<label>
206
<input {...input} type="checkbox" />
207
{label}
208
</label>
209
);
210
}
211
212
// Form using custom field components
213
function MyForm() {
214
const required = (value: any) => (value ? undefined : "Required");
215
216
return (
217
<Form onSubmit={(values) => console.log(values)}>
218
{({ handleSubmit }) => (
219
<form onSubmit={handleSubmit}>
220
<CustomTextField name="firstName" placeholder="First Name" />
221
<ValidatedInput
222
name="email"
223
type="email"
224
validate={required}
225
placeholder="Email"
226
/>
227
<CurrencyInput name="salary" />
228
<CustomCheckbox name="subscribe" label="Subscribe to newsletter" />
229
<button type="submit">Submit</button>
230
</form>
231
)}
232
</Form>
233
);
234
}
235
```
236
237
### useFormState Hook
238
239
Hook for subscribing to form state changes with customizable subscriptions.
240
241
```typescript { .api }
242
/**
243
* Hook for form state subscription with performance optimization
244
* @param params - Configuration for form state subscription
245
* @returns Current form state based on subscription
246
*/
247
function useFormState<FormValues = Record<string, any>>(
248
params?: UseFormStateParams<FormValues>
249
): FormState<FormValues>;
250
251
interface UseFormStateParams<FormValues = Record<string, any>> {
252
/** Callback when form state changes */
253
onChange?: (formState: FormState<FormValues>) => void;
254
/** Form state subscription configuration */
255
subscription?: FormSubscription;
256
}
257
```
258
259
**Usage Examples:**
260
261
```typescript
262
import React from "react";
263
import { Form, Field, useFormState } from "react-final-form";
264
265
// Basic useFormState usage
266
function FormStatus() {
267
const { dirty, invalid, submitting } = useFormState();
268
269
return (
270
<div>
271
<p>Form is {dirty ? "dirty" : "pristine"}</p>
272
<p>Form is {invalid ? "invalid" : "valid"}</p>
273
{submitting && <p>Submitting...</p>}
274
</div>
275
);
276
}
277
278
// useFormState with subscription
279
function OptimizedFormStatus() {
280
const { submitting, pristine, invalid } = useFormState({
281
subscription: { submitting: true, pristine: true, invalid: true }
282
});
283
284
return (
285
<button type="submit" disabled={submitting || pristine || invalid}>
286
{submitting ? "Submitting..." : "Submit"}
287
</button>
288
);
289
}
290
291
// useFormState with onChange callback
292
function FormMonitor() {
293
const formState = useFormState({
294
onChange: (state) => {
295
console.log("Form state changed:", state);
296
// Auto-save, analytics, etc.
297
}
298
});
299
300
return (
301
<div>
302
<h3>Form Monitor</h3>
303
<pre>{JSON.stringify(formState, null, 2)}</pre>
304
</div>
305
);
306
}
307
308
// useFormState for conditional rendering
309
function ConditionalFields() {
310
const { values } = useFormState({ subscription: { values: true } });
311
312
return (
313
<div>
314
{values.showAdvanced && (
315
<div>
316
<Field name="advancedOption1" component="input" />
317
<Field name="advancedOption2" component="input" />
318
</div>
319
)}
320
</div>
321
);
322
}
323
324
// Complete form with hooks
325
function HookBasedForm() {
326
return (
327
<Form onSubmit={(values) => console.log(values)}>
328
{({ handleSubmit }) => (
329
<form onSubmit={handleSubmit}>
330
<Field name="name" component="input" placeholder="Name" />
331
<Field name="showAdvanced" component="input" type="checkbox" />
332
333
<ConditionalFields />
334
<FormStatus />
335
<FormMonitor />
336
337
<button type="submit">Submit</button>
338
</form>
339
)}
340
</Form>
341
);
342
}
343
```
344
345
### Hook Configuration Options
346
347
All hooks support comprehensive configuration options for optimization and customization.
348
349
```typescript { .api }
350
/**
351
* Field subscription configuration for useField hook
352
*/
353
interface FieldSubscription {
354
active?: boolean;
355
data?: boolean;
356
dirty?: boolean;
357
dirtySinceLastSubmit?: boolean;
358
error?: boolean;
359
initial?: boolean;
360
invalid?: boolean;
361
length?: boolean;
362
modified?: boolean;
363
modifiedSinceLastSubmit?: boolean;
364
pristine?: boolean;
365
submitError?: boolean;
366
submitFailed?: boolean;
367
submitSucceeded?: boolean;
368
submitting?: boolean;
369
touched?: boolean;
370
valid?: boolean;
371
validating?: boolean;
372
value?: boolean;
373
visited?: boolean;
374
}
375
376
/**
377
* Form subscription configuration for useFormState hook
378
*/
379
interface FormSubscription {
380
active?: boolean;
381
dirty?: boolean;
382
dirtyFields?: boolean;
383
dirtySinceLastSubmit?: boolean;
384
error?: boolean;
385
errors?: boolean;
386
hasSubmitErrors?: boolean;
387
hasValidationErrors?: boolean;
388
initialValues?: boolean;
389
invalid?: boolean;
390
modified?: boolean;
391
modifiedSinceLastSubmit?: boolean;
392
pristine?: boolean;
393
submitError?: boolean;
394
submitErrors?: boolean;
395
submitFailed?: boolean;
396
submitSucceeded?: boolean;
397
submitting?: boolean;
398
touched?: boolean;
399
valid?: boolean;
400
validating?: boolean;
401
values?: boolean;
402
visited?: boolean;
403
}
404
```
405
406
### Hook Error Handling
407
408
Hooks provide clear error messages when used incorrectly.
409
410
**Usage Examples:**
411
412
```typescript
413
// Error handling example
414
function SafeHookUsage() {
415
try {
416
const form = useForm("SafeComponent");
417
return <div>Form API available</div>;
418
} catch (error) {
419
return <div>Error: Component must be used inside a Form</div>;
420
}
421
}
422
423
// Conditional hook usage (NOT RECOMMENDED - breaks rules of hooks)
424
// Instead, always use hooks at the top level and handle conditions in render
425
function CorrectConditionalUsage() {
426
const form = useForm(); // Always call hooks at top level
427
const { values } = useFormState();
428
429
if (!values.needsField) {
430
return <div>Field not needed</div>;
431
}
432
433
// Use hook results conditionally, not the hooks themselves
434
return (
435
<div>
436
<button onClick={() => form.reset()}>Reset</button>
437
</div>
438
);
439
}
440
```
441
442
### Performance Optimization with Hooks
443
444
Hooks support performance optimization through subscriptions and memoization.
445
446
**Usage Examples:**
447
448
```typescript
449
import React from "react";
450
451
// Optimized field component
452
function OptimizedField({ name }: { name: string }) {
453
const { input, meta } = useField(name, {
454
subscription: { value: true, error: true, touched: true }
455
});
456
457
return React.useMemo(() => (
458
<div>
459
<input {...input} />
460
{meta.error && meta.touched && <span>{meta.error}</span>}
461
</div>
462
), [input.value, meta.error, meta.touched]);
463
}
464
465
// Memoized form status component
466
const FormStatusMemo = React.memo(() => {
467
const { dirty, invalid, submitting } = useFormState({
468
subscription: { dirty: true, invalid: true, submitting: true }
469
});
470
471
return (
472
<div>
473
Status: {dirty ? "Modified" : "Unchanged"} |
474
{invalid ? "Invalid" : "Valid"} |
475
{submitting ? "Submitting" : "Ready"}
476
</div>
477
);
478
});
479
```