@@ -276,6 +276,66 @@ Dynamic array field management component with complete array operations.
276276
277277Computed 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