Skip to content

Commit 4e0320e

Browse files
committed
feat(docs): enhance README with detailed props and examples for ComputedField and useArrayField hooks
1 parent ed5c4e2 commit 4e0320e

File tree

2 files changed

+472
-4
lines changed

2 files changed

+472
-4
lines changed

primitives/filed-form/README.md

Lines changed: 236 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,66 @@ Dynamic array field management component with complete array operations.
276276

277277
Computed field component that automatically calculates and updates based on other field values.
278278

279+
**Props:**
280+
281+
| Property | Type | Default | Description |
282+
|----------|------|---------|-------------|
283+
| `name` | `string` | **Required** | Computed field name |
284+
| `deps` | `string[]` | **Required** | Array of dependent field names |
285+
| `compute` | `(get, all) => any` | **Required** | Compute function |
286+
| `rules` | `Rule[]` | - | Validation rules array |
287+
| `valuePropName` | `string` | `'value'` | Name of the value prop |
288+
| `preserve` | `boolean` | `true` | Whether to preserve value on unmount |
289+
290+
**Examples:**
291+
292+
```tsx
293+
// Calculate total price
294+
<Form initialValues={{ quantity: 1, unitPrice: 100 }}>
295+
<Field name="quantity">
296+
<input type="number" placeholder="Quantity" />
297+
</Field>
298+
299+
<Field name="unitPrice">
300+
<input type="number" placeholder="Unit Price" />
301+
</Field>
302+
303+
<ComputedField
304+
name="totalPrice"
305+
deps={['quantity', 'unitPrice']}
306+
compute={(get) => {
307+
const quantity = get('quantity') || 0;
308+
const unitPrice = get('unitPrice') || 0;
309+
return quantity * unitPrice;
310+
}}
311+
>
312+
<input placeholder="Total Price (auto-calculated)" />
313+
</ComputedField>
314+
</Form>
315+
316+
// Format full name
317+
<Form>
318+
<Field name="firstName">
319+
<input placeholder="First Name" />
320+
</Field>
321+
<Field name="lastName">
322+
<input placeholder="Last Name" />
323+
</Field>
324+
325+
<ComputedField
326+
name="fullName"
327+
deps={['firstName', 'lastName']}
328+
compute={(get) => {
329+
const first = get('firstName') || '';
330+
const last = get('lastName') || '';
331+
return `${first} ${last}`.trim();
332+
}}
333+
>
334+
<input placeholder="Full Name (auto-generated)" />
335+
</ComputedField>
336+
</Form>
337+
```
338+
279339
### Hooks
280340

281341
#### `useForm()`
@@ -346,10 +406,184 @@ const error = useFieldError('email', { form });
346406

347407
#### `useArrayField()`
348408

349-
Hook for array field operations.
409+
Hook for array field operations, providing complete array operation capabilities.
410+
411+
```tsx
412+
const { fields, add, remove, move, swap, insert, replace } = useArrayField('users', form);
413+
414+
// Return values
415+
// fields: ListRenderItem[] - Array field items list (with stable keys)
416+
// add: (value?) => void - Add new item
417+
// remove: (index) => void - Remove item at index
418+
// move: (from, to) => void - Move item position
419+
// swap: (i, j) => void - Swap two items
420+
// insert: (index, value) => void - Insert at specific position
421+
// replace: (index, value) => void - Replace item at index
422+
```
423+
424+
**Examples:**
425+
426+
```tsx
427+
function TodoList() {
428+
const [form] = useForm();
429+
const { fields, add, remove, move } = useArrayField('todos', form);
430+
431+
return (
432+
<Form form={form} initialValues={{ todos: [] }}>
433+
{fields.map((field, index) => (
434+
<div key={field.key}>
435+
<Field name={`${field.name}.title`}>
436+
<input placeholder="Task Title" />
437+
</Field>
438+
<button onClick={() => remove(index)}>Delete</button>
439+
<button onClick={() => move(index, index - 1)}>Move Up</button>
440+
</div>
441+
))}
442+
<button onClick={() => add({ title: '' })}>Add Task</button>
443+
</Form>
444+
);
445+
}
446+
```
447+
448+
#### `useFieldEffect()`
449+
450+
Create reactive side effects that execute custom logic when specified fields change.
451+
452+
```tsx
453+
useFieldEffect(
454+
deps: string[], // Array of dependent field names
455+
effect: (get, all) => void, // Effect function
456+
form?: FormInstance // Optional form instance
457+
);
458+
```
459+
460+
**Examples:**
461+
462+
```tsx
463+
function UserForm() {
464+
const [form] = useForm();
465+
466+
// Log when username changes
467+
useFieldEffect(
468+
['firstName', 'lastName'],
469+
(get) => {
470+
const firstName = get('firstName');
471+
const lastName = get('lastName');
472+
console.log(`Name changed to: ${firstName} ${lastName}`);
473+
},
474+
form
475+
);
476+
477+
// Clear city when country changes
478+
useFieldEffect(
479+
['country'],
480+
(get, all) => {
481+
const country = get('country');
482+
if (country !== all.previousCountry) {
483+
form.setFieldValue('city', undefined);
484+
}
485+
},
486+
form
487+
);
488+
489+
return (
490+
<Form form={form}>
491+
<Field name="firstName">
492+
<input placeholder="First Name" />
493+
</Field>
494+
<Field name="lastName">
495+
<input placeholder="Last Name" />
496+
</Field>
497+
<Field name="country">
498+
<select>{/* Country options */}</select>
499+
</Field>
500+
<Field name="city">
501+
<select>{/* City options */}</select>
502+
</Field>
503+
</Form>
504+
);
505+
}
506+
```
507+
508+
#### `useSelector()`
509+
510+
Select and subscribe to derived form state, only triggering re-renders when the selected value changes.
511+
512+
```tsx
513+
const value = useSelector<Values, Result>(
514+
selector: (get, all) => Result, // Selector function
515+
options?: {
516+
deps?: string[]; // Dependent fields (empty array = all fields)
517+
form?: FormInstance; // Form instance
518+
eq?: (a, b) => boolean; // Custom equality comparison function
519+
mask?: ChangeMask; // Change type bitmask to subscribe to
520+
includeChildren?: boolean; // Whether to include child field changes
521+
}
522+
);
523+
```
524+
525+
**Examples:**
350526

351527
```tsx
352-
const { fields, add, remove, move } = useArrayField('users', { form });
528+
// Calculate shopping cart total
529+
function ShoppingCart() {
530+
const total = useSelector(
531+
(get) => {
532+
const items = get('items') || [];
533+
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
534+
},
535+
{ deps: ['items'] }
536+
);
537+
538+
return (
539+
<Form>
540+
<List name="items">
541+
{(fields, { add, remove }) => (
542+
<>
543+
{fields.map((field) => (
544+
<div key={field.key}>
545+
<Field name={`${field.name}.name`}>
546+
<input placeholder="Product Name" />
547+
</Field>
548+
<Field name={`${field.name}.price`}>
549+
<input type="number" placeholder="Price" />
550+
</Field>
551+
<Field name={`${field.name}.quantity`}>
552+
<input type="number" placeholder="Quantity" />
553+
</Field>
554+
<button onClick={() => remove(field.name)}>Delete</button>
555+
</div>
556+
))}
557+
<button onClick={() => add({ name: '', price: 0, quantity: 1 })}>
558+
Add Product
559+
</button>
560+
</>
561+
)}
562+
</List>
563+
<div>Total: ${total.toFixed(2)}</div>
564+
</Form>
565+
);
566+
}
567+
568+
// Use custom equality comparison (avoid re-renders from object reference changes)
569+
function UserProfile() {
570+
const userInfo = useSelector(
571+
(get) => ({
572+
name: get('name'),
573+
age: get('age'),
574+
email: get('email')
575+
}),
576+
{
577+
deps: ['name', 'age', 'email'],
578+
eq: (a, b) => {
579+
// Deep equality comparison
580+
return a.name === b.name && a.age === b.age && a.email === b.email;
581+
}
582+
}
583+
);
584+
585+
return <div>{JSON.stringify(userInfo)}</div>;
586+
}
353587
```
354588

355589
#### `useUndoRedo()`

0 commit comments

Comments
 (0)