import React from 'react'; import type { FormErrors, FormFieldValue, FormValues, UseFormControllerOptions, UseFormControllerResult, } from '../../types/form'; import { validateFields } from './utils/validation'; function createInitialValues( fields: readonly { name: TName }[], initialValues: Partial> | undefined, ): FormValues { const values = fields.reduce>((nextValues, field) => { nextValues[field.name] = initialValues?.[field.name] ?? ''; return nextValues; }, {}); return values as FormValues; } export function useFormController({ fields, initialValues, values: controlledValues, errors: externalErrors, onChange, onSubmit, validateOnChange = false, }: UseFormControllerOptions): UseFormControllerResult { const [initialFormValues] = React.useState>(() => createInitialValues(fields, initialValues), ); const [internalValues, setInternalValues] = React.useState>( () => initialFormValues, ); const [validationErrors, setValidationErrors] = React.useState>({}); const values = controlledValues ?? internalValues; const errors = React.useMemo( () => ({ ...validationErrors, ...externalErrors, }), [externalErrors, validationErrors], ); const setValues = React.useCallback( (nextValues: FormValues) => { if (!controlledValues) { setInternalValues(nextValues); } onChange?.(nextValues); }, [controlledValues, onChange], ); const validate = React.useCallback(() => { const result = validateFields(fields, values); setValidationErrors(result.errors); return result; }, [fields, values]); const setFieldValue = React.useCallback( (name: TName, value: FormFieldValue) => { const nextValues = { ...values, [name]: value, }; setValues(nextValues); if (validateOnChange) { const result = validateFields(fields, nextValues); setValidationErrors(result.errors); } }, [fields, setValues, validateOnChange, values], ); const handleSubmit = React.useCallback(async () => { const result = validate(); if (!result.valid) { return; } await onSubmit?.(values); }, [onSubmit, validate, values]); const reset = React.useCallback(() => { setValidationErrors({}); setValues(initialFormValues); }, [initialFormValues, setValues]); return { values, errors, setValues, setFieldValue, validate, handleSubmit, reset, }; }