UNPKG

@matthew.ngo/reform

Version:

A flexible and powerful React form management library with advanced validation, state observation, and multi-group support

349 lines (296 loc) 8.68 kB
# Reform Field Arrays Reform provides comprehensive support for managing array fields within form groups. This document covers the field array capabilities of Reform. ## Table of Contents - [Overview](#overview) - [Basic Usage](#basic-usage) - [Array Operations](#array-operations) - [Dynamic Arrays](#dynamic-arrays) - [Validation](#validation) - [API Reference](#api-reference) ## Overview Reform's field array system allows you to: 1. **Manage array fields** within form groups 2. **Perform array operations** (append, remove, move, etc.) 3. **Handle validation** for array items 4. **Track changes** in array fields ## Basic Usage Here's a basic example of using array fields in a form: ```tsx import { useReform } from '@reform/core'; type UserForm = { name: string; phones: string[]; addresses: { street: string; city: string; }[]; }; const UserForm = () => { const form = useReform<UserForm>({ defaultData: { name: '', phones: [], addresses: [], }, minGroups: 1, }); // Add a new phone number const handleAddPhone = () => { form.append(0, 'phones', ''); }; // Add a new address const handleAddAddress = () => { form.append(0, 'addresses', { street: '', city: '' }); }; return ( <form> <input {...form.register(0, 'name')} placeholder="Name" /> {/* Phone numbers */} <div> <h3>Phone Numbers</h3> {form.getArray(0, 'phones').map((phone, phoneIndex) => ( <div key={phoneIndex}> <input {...form.register(0, `phones.${phoneIndex}`)} placeholder="Phone number" /> <button type="button" onClick={() => form.remove(0, 'phones', phoneIndex)} > Remove </button> </div> ))} <button type="button" onClick={handleAddPhone}> Add Phone </button> </div> {/* Addresses */} <div> <h3>Addresses</h3> {form.getArray(0, 'addresses').map((address, addressIndex) => ( <div key={addressIndex}> <input {...form.register(0, `addresses.${addressIndex}.street`)} placeholder="Street" /> <input {...form.register(0, `addresses.${addressIndex}.city`)} placeholder="City" /> <button type="button" onClick={() => form.remove(0, 'addresses', addressIndex)} > Remove </button> </div> ))} <button type="button" onClick={handleAddAddress}> Add Address </button> </div> </form> ); }; ``` ## Array Operations Reform provides several operations for managing array fields: ```tsx // Append an item to the end of the array form.append(0, 'phones', '555-0123'); // Remove an item at a specific index form.remove(0, 'phones', 2); // Update an item at a specific index form.update(0, 'phones', 1, '555-4567'); // Move an item from one position to another form.move(0, 'phones', 0, 2); // Swap two items form.swap(0, 'phones', 1, 3); // Insert an item at a specific position form.insert(0, 'phones', 1, '555-8901'); // Replace the entire array form.replace(0, 'phones', ['555-0123', '555-4567', '555-8901']); ``` ## Dynamic Arrays Example of a dynamic form with array fields: ```tsx import { useReform } from '@reform/core'; type EducationForm = { schools: { name: string; degree: string; years: { from: number; to: number; }[]; }[]; }; const EducationForm = () => { const form = useReform<EducationForm>({ defaultData: { schools: [], }, minGroups: 1, }); const handleAddSchool = () => { form.append(0, 'schools', { name: '', degree: '', years: [], }); }; const handleAddYear = (schoolIndex: number) => { form.append(0, `schools.${schoolIndex}.years`, { from: new Date().getFullYear(), to: new Date().getFullYear(), }); }; return ( <form> <h2>Education History</h2> {form.getArray(0, 'schools').map((school, schoolIndex) => ( <div key={schoolIndex} className="school-entry"> <h3>School {schoolIndex + 1}</h3> <input {...form.register(0, `schools.${schoolIndex}.name`)} placeholder="School name" /> <input {...form.register(0, `schools.${schoolIndex}.degree`)} placeholder="Degree" /> <div className="years"> <h4>Years Attended</h4> {form.getArray(0, `schools.${schoolIndex}.years`).map((year, yearIndex) => ( <div key={yearIndex} className="year-entry"> <input type="number" {...form.register(0, `schools.${schoolIndex}.years.${yearIndex}.from`)} placeholder="From year" /> <input type="number" {...form.register(0, `schools.${schoolIndex}.years.${yearIndex}.to`)} placeholder="To year" /> <button type="button" onClick={() => form.remove(0, `schools.${schoolIndex}.years`, yearIndex)} > Remove Year </button> </div> ))} <button type="button" onClick={() => handleAddYear(schoolIndex)} > Add Year </button> </div> <button type="button" onClick={() => form.remove(0, 'schools', schoolIndex)} > Remove School </button> </div> ))} <button type="button" onClick={handleAddSchool}> Add School </button> </form> ); }; ``` ## Validation Example of array field validation: ```tsx import { useReform } from '@reform/core'; import * as yup from 'yup'; const schema = yup.object({ phones: yup.array().of( yup.string().matches(/^\d{3}-\d{4}$/, 'Invalid phone format (XXX-XXXX)') ).min(1, 'At least one phone number is required'), addresses: yup.array().of( yup.object({ street: yup.string().required('Street is required'), city: yup.string().required('City is required'), }) ).min(1, 'At least one address is required'), }); const MyForm = () => { const form = useReform<UserForm>({ defaultData: { phones: [], addresses: [], }, minGroups: 1, groupSchema: schema, }); // ... form implementation }; ``` ## API Reference ### Field Array Methods ```typescript interface FieldArrayHelpers<T> { getArray<K extends keyof T>( index: number, field: K ): any[]; append<K extends keyof T>( index: number, field: K, value: any, options?: { shouldFocus?: boolean } ): void; remove<K extends keyof T>( index: number, field: K, arrayIndex: number ): void; update<K extends keyof T>( index: number, field: K, arrayIndex: number, value: any ): void; move<K extends keyof T>( index: number, field: K, from: number, to: number ): void; swap<K extends keyof T>( index: number, field: K, indexA: number, indexB: number ): void; insert<K extends keyof T>( index: number, field: K, arrayIndex: number, value: any ): void; replace<K extends keyof T>( index: number, field: K, newArray: any[] ): void; } ``` ### Method Parameters | Method | Parameters | Description | |--------|------------|-------------| | getArray | `index`: number, `field`: keyof T | Gets the array value for a specific field | | append | `index`: number, `field`: keyof T, `value`: any, `options?`: { shouldFocus?: boolean } | Adds a new item to the end of the array | | remove | `index`: number, `field`: keyof T, `arrayIndex`: number | Removes an item at the specified index | | update | `index`: number, `field`: keyof T, `arrayIndex`: number, `value`: any | Updates an item at the specified index | | move | `index`: number, `field`: keyof T, `from`: number, `to`: number | Moves an item from one position to another | | swap | `index`: number, `field`: keyof T, `indexA`: number, `indexB`: number | Swaps two items in the array | | insert | `index`: number, `field`: keyof T, `arrayIndex`: number, `value`: any | Inserts a new item at the specified position | | replace | `index`: number, `field`: keyof T, `newArray`: any[] | Replaces the entire array with a new one |