UNPKG

@matthew.ngo/reform

Version:

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

397 lines (321 loc) 10.5 kB
# Reform Validation Reform provides a powerful validation system that helps you validate form data with multiple strategies. This document covers the validation capabilities of Reform. ## Table of Contents - [Overview](#overview) - [Yup Integration](#yup-integration) - [Dynamic Validation](#dynamic-validation) - [Context-Aware Validation](#context-aware-validation) - [Field-Level Validation](#field-level-validation) - [Group Validation](#group-validation) - [Form Submission Validation](#form-submission-validation) - [API Reference](#api-reference) ## Overview Reform's validation system offers several validation strategies: 1. **Schema-based validation** with Yup 2. **Dynamic validation rules** based on field values 3. **Context-aware validation** that can access other form data 4. **Field-level validation** for individual fields 5. **Group validation** for validating entire form groups 6. **Form submission validation** for pre-submission checks ## Yup Integration Reform provides seamless integration with Yup for schema-based validation. ### Basic Usage ```tsx import { useReform } from '@reform/core'; import * as yup from 'yup'; // Define your validation schema const userSchema = yup.object({ name: yup.string().required('Name is required'), email: yup.string().email('Invalid email').required('Email is required'), age: yup.number().min(18, 'Must be at least 18 years old'), }); const MyForm = () => { const form = useReform<UserForm>({ defaultData: { name: '', email: '', age: 0 }, minGroups: 1, groupSchema: userSchema, }); return ( <form onSubmit={form.formMethods.handleSubmit(onSubmit)}> {/* Form fields */} </form> ); }; ``` ### Enhanced Yup Schema Reform extends Yup's functionality with transformers and context: ```tsx import { useReform } from '@reform/core'; import * as yup from 'yup'; const MyForm = () => { const form = useReform<UserForm>(config); // Create an enhanced schema with transformers and context const enhancedSchema = form.yup.createEnhancedSchema( yup.object({ birthDate: yup.date().required('Birth date is required'), registrationDate: yup.date().min( yup.ref('birthDate'), 'Registration date must be after birth date' ), }) ); // Register a transformer for date fields useEffect(() => { const unregister = form.yup.registerTransformer({ field: 'birthDate', transformer: (value) => value instanceof Date ? value : new Date(value), transformOn: 'input' }); return unregister; }, []); return ( <form> {/* Form fields */} </form> ); }; ``` ## Dynamic Validation Reform allows you to create dynamic validation rules that change based on form values. ```tsx import { useReform, useDynamicValidation } from '@reform/core'; const MyForm = () => { const form = useReform<UserForm>(config); const dynamicValidation = useDynamicValidation(form, { rules: [ { field: 'password', validate: (value, data) => { if (!value) return 'Password is required'; if (value.length < 8) return 'Password must be at least 8 characters'; return null; }, }, { field: 'confirmPassword', validate: (value, data, context) => { if (value !== data.password) { return 'Passwords do not match'; } return null; }, dependencies: ['password'], }, ], }); return ( <form> {/* Form fields */} </form> ); }; ``` ## Context-Aware Validation Reform's validation system can access context data from other parts of the form: ```tsx import { useReform } from '@reform/core'; import * as yup from 'yup'; const MyForm = () => { const form = useReform<UserForm>(config); // Update context data useEffect(() => { form.yup.updateContextData({ minAge: 18, maxAge: 65, currentDate: new Date(), }); }, []); // Create a schema that uses context const schema = form.yup.createSchemaWithContext( yup.object({ age: yup.number().test( 'age-range', 'Age must be between ${context.minAge} and ${context.maxAge}', function(value) { const { minAge, maxAge } = this.options.context; return value >= minAge && value <= maxAge; } ), }) ); return ( <form> {/* Form fields */} </form> ); }; ``` ## Field-Level Validation Reform supports field-level validation through React Hook Form's validation rules: ```tsx import { useReform } from '@reform/core'; const MyForm = () => { const form = useReform<UserForm>(config); return ( <form> <input {...form.register(0, 'email', { required: 'Email is required', pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: 'Invalid email address', }, })} placeholder="Email" /> {form.getFieldError(0, 'email') && ( <p>{form.getFieldError(0, 'email')}</p> )} </form> ); }; ``` ## Group Validation Reform provides utilities to validate entire form groups and check if they meet specific requirements: ```tsx import { useReform } from '@reform/core'; const MyForm = () => { const form = useReform<UserForm>(config); const { isGroupValid, areAllGroupsValid } = form.errors; // Define required fields for validation const requiredFields: (keyof UserForm)[] = ['name', 'email', 'age']; // Check if a specific group is valid const isFirstGroupValid = isGroupValid(0, requiredFields); // Check if all groups are valid const allGroupsValid = areAllGroupsValid(requiredFields); const handleAddGroup = () => { // Only add a new group if all existing groups are valid if (allGroupsValid) { form.addGroup(); } else { alert('Please complete all required fields in existing groups first'); } }; return ( <form> {/* Form fields */} <button type="button" onClick={handleAddGroup} disabled={!allGroupsValid} > Add Group </button> </form> ); }; ``` ## Form Submission Validation Reform provides utilities to check if a form is ready for submission: ```tsx import { useReform } from '@reform/core'; const MyForm = () => { const form = useReform<UserForm>(config); const { isFormSubmittable } = form.errors; // Define required fields for submission const requiredFields: (keyof UserForm)[] = ['name', 'email', 'age']; // Check if the form can be submitted const canSubmit = isFormSubmittable(requiredFields); const handleSubmit = (data: any) => { console.log('Form submitted:', data); }; return ( <form onSubmit={form.formMethods.handleSubmit(handleSubmit)}> {/* Form fields */} <button type="submit" disabled={!canSubmit} > Submit </button> {!canSubmit && ( <p className="error"> Please complete all required fields and fix any errors before submitting </p> )} </form> ); }; ``` ## API Reference ### useReformYupIntegration ```typescript function useReformYupIntegration<T extends Record<string, any> & AnyObject>( reform: ReformReturn<T> ): ReformYupIntegration<T> ``` #### Returns Returns a `ReformYupIntegration<T>` object with the following methods: | Method | Description | |--------|-------------| | createEnhancedSchema | Create a schema with transformers and context | | validateWithContext | Validate data with context for a specific group | | updateContextData | Update context data for both context and transformers | | validateAllGroups | Validate all groups with the current schema | | createSchemaWithContext | Create a schema with context support | | createSchemaWithTransformers | Create a schema with transformer support | | registerTransformer | Register a transformer for a specific field | | unregisterTransformer | Unregister a transformer | ### useDynamicValidation ```typescript function useDynamicValidation<T extends Record<string, any>>( reform: ReformReturn<T>, config: DynamicSchemaConfig<T> ): { validateField(groupIndex: number, field: FieldPath<T>, value: any): ValidationRuleResult; validateGroup(groupIndex: number): boolean; getFieldRules(groupIndex: number, field: FieldPath<T>): DynamicValidationRule<T>[]; updateContext(newContextData: Record<string, any>): void; } ``` #### Parameters | Parameter | Type | Description | |-----------|------|-------------| | reform | `ReformReturn<T>` | Reform hook return value | | config | `DynamicSchemaConfig<T>` | Configuration for dynamic validation | #### Configuration Options | Option | Type | Description | |--------|------|-------------| | rules | `DynamicValidationRule<T>[]` | Array of validation rules | | context | `Record<string, any>` | Context data for validation | ### FormErrorsState ```typescript interface FormErrorsState<T extends Record<string, any>> { errors: Record<string, FieldError>; hasErrors: boolean; getFieldError: (groupIndex: number, fieldName: string) => string | undefined; getGroupErrors: (index: number) => Record<string, string>; getFieldErrors: (index: number) => Record<keyof T, string | undefined>; clearErrors: () => void; clearFieldError: (groupIndex: number, fieldName: string) => void; clearGroupError: (groupIndex: number) => void; clearGlobalError: () => void; getError: (groupIndex: number, field: string) => string | undefined; hasGroupErrors: (groupIndex: number) => boolean; isGroupValid: (groupIndex: number, requiredFields: (keyof T)[]) => boolean; areAllGroupsValid: (requiredFields: (keyof T)[]) => boolean; isFormSubmittable: (requiredFields: (keyof T)[]) => boolean; canAddNewGroup: (requiredFields: (keyof T)[]) => boolean; } ``` ### DynamicValidationRule ```typescript interface DynamicValidationRule<T> { field: FieldPath<T>; validate: ( value: any, data: T, context: ValidationContext ) => string | null | Promise<string | null>; dependencies?: Array<FieldPath<T>>; runCondition?: (data: T, context: ValidationContext) => boolean; } ``` ### YupTransformer ```typescript interface YupTransformer<T> { field: FieldPath<T>; transformer: (value: any) => any; transformOn: 'input' | 'output' | 'both'; } ```