/** * useFormField Hook * Individual form field state management with validation * * @example * const emailField = useFormField({ * name: 'email', * defaultValue: '', * rules: [ * { validate: (v) => v.length > 0, message: 'Email is required' }, * { validate: (v) => v.includes('@'), message: 'Invalid email format' } * ] * }) * * emailField.onChange(e.target.value)} * onBlur={emailField.onBlur} * /> * {emailField.error && {emailField.error}} */ import { useState, useCallback } from 'react' export interface ValidationRule { /** Validation function returning true if valid */ validate: (value: T) => boolean /** Error message to display when validation fails */ message: string } export interface FieldConfig { /** Field name identifier */ name: string /** Default/initial value */ defaultValue?: T /** Array of validation rules */ rules?: ValidationRule[] } export interface UseFormFieldReturn { /** Current field value */ value: T | undefined /** Current error message, or null if valid */ error: string | null /** Whether the field has been touched (blurred) */ touched: boolean /** Handle value change */ onChange: (newValue: T) => void /** Handle blur event */ onBlur: () => void /** Reset field to initial state */ reset: () => void /** Validate the field and return validity */ validate: () => boolean /** Whether the field is currently valid */ isValid: boolean /** Whether the value differs from default */ isDirty: boolean } /** * Hook for managing individual form field state * @param config - Field configuration * @returns Object containing field state and control methods */ export function useFormField(config: FieldConfig): UseFormFieldReturn { const [value, setValue] = useState(config.defaultValue) const [error, setError] = useState(null) const [touched, setTouched] = useState(false) const validate = useCallback(() => { if (!config.rules || !touched) return true for (const rule of config.rules) { if (!rule.validate(value as T)) { setError(rule.message) return false } } setError(null) return true }, [value, config.rules, touched]) const onChange = useCallback((newValue: T) => { setValue(newValue) if (touched) { setError(null) } }, [touched]) const onBlur = useCallback(() => { setTouched(true) validate() }, [validate]) const reset = useCallback(() => { setValue(config.defaultValue) setError(null) setTouched(false) }, [config.defaultValue]) return { value, error, touched, onChange, onBlur, reset, validate, isValid: error === null, isDirty: value !== config.defaultValue, } } /** * useForm Hook * Form submission state management */ export interface FormConfig { /** Field configurations keyed by field name */ fields: Record /** Submit handler */ onSubmit?: (values: Record) => void | Promise } export interface UseFormReturn { /** Submit the form */ submit: (values: Record) => Promise /** Whether form is currently submitting */ isSubmitting: boolean } /** * Hook for managing form submission state * @param config - Form configuration * @returns Object containing form state and submit method */ export function useForm(config: FormConfig): UseFormReturn { const [isSubmitting, setIsSubmitting] = useState(false) const submit = useCallback(async (values: Record) => { setIsSubmitting(true) try { await config.onSubmit?.(values) } finally { setIsSubmitting(false) } }, [config]) return { submit, isSubmitting, } }