UNPKG

react-antd-admin-panel

Version:

Modern TypeScript-first React admin panel builder with Ant Design 6

155 lines 4.95 kB
import { useState, useCallback, useRef } from 'react'; import { useForm as useReactHookForm } from 'react-hook-form'; import { Post } from '../http/Post'; /** * useForm - React hook for form state management * * Wraps react-hook-form with a simplified API and optional Post integration. * * @template T - Form values type * @param options - Hook options * @returns Hook result with form state and control functions * * @example * ```tsx * // Basic usage * const { values, setValue, submit, submitting } = useForm({ * initialValues: { name: '', email: '' }, * onSubmit: async (values) => { * await saveUser(values); * }, * }); * * // With Post integration * const { values, submit, submitting } = useForm({ * initialValues: { name: '', email: '' }, * post: { * url: '/api/users', * onSuccess: (user) => navigate(`/users/${user.id}`), * }, * }); * * // With validation * const { values, errors, submit } = useForm({ * initialValues: { email: '' }, * validate: (values) => { * const errors: Record<string, string> = {}; * if (!values.email.includes('@')) { * errors.email = 'Invalid email'; * } * return errors; * }, * }); * * // In JSX * <input * value={values.name} * onChange={(e) => setValue('name', e.target.value)} * /> * <span>{errors.name?.message}</span> * <button onClick={submit} disabled={submitting}>Save</button> * ``` */ export function useForm(options) { const { initialValues, mode = 'onSubmit', } = options; const [submitting, setSubmitting] = useState(false); const optionsRef = useRef(options); optionsRef.current = options; // Use react-hook-form under the hood const form = useReactHookForm({ defaultValues: initialValues, mode, }); const { register, reset: rhfReset, formState: { errors, isDirty, isValid }, setValue: rhfSetValue, getValues, getFieldState, trigger, clearErrors, setError: rhfSetError, watch, } = form; /** * Set a single field value */ const setValue = useCallback((name, value) => { rhfSetValue(name, value, { shouldValidate: true, shouldDirty: true }); }, [rhfSetValue]); /** * Set multiple field values */ const setValues = useCallback((values) => { Object.entries(values).forEach(([key, value]) => { rhfSetValue(key, value, { shouldValidate: true, shouldDirty: true }); }); }, [rhfSetValue]); /** * Submit the form */ const submit = useCallback(async () => { const currentOptions = optionsRef.current; // Trigger validation first const isFormValid = await trigger(); if (!isFormValid) { return; } const values = getValues(); // Run custom validation if provided if (currentOptions.validate) { const validationErrors = await currentOptions.validate(values); if (Object.keys(validationErrors).length > 0) { // Set errors on form Object.entries(validationErrors).forEach(([field, message]) => { rhfSetError(field, { type: 'validate', message }); }); return; } } setSubmitting(true); try { // Use Post if configured if (currentOptions.post) { const postInstance = new Post() .target(currentOptions.post.url) .method(currentOptions.post.method || 'POST') .body(values); if (currentOptions.post.headers) { postInstance.headers(currentOptions.post.headers); } const response = await postInstance.execute(); currentOptions.post.onSuccess?.(response); } else if (currentOptions.onSubmit) { // Use onSubmit callback await currentOptions.onSubmit(values); } } catch (error) { if (currentOptions.post?.onError) { currentOptions.post.onError(error instanceof Error ? error : new Error(String(error))); } throw error; } finally { setSubmitting(false); } }, [trigger, getValues, rhfSetError]); /** * Reset form to initial or provided values */ const reset = useCallback((values) => { rhfReset(values ?? initialValues); }, [rhfReset, initialValues]); // Get current values (reactive) const values = watch(); return { values: values, errors, setValue, setValues, submit, submitting, reset, isDirty, isValid, register, getFieldState, trigger, clearErrors, setError: rhfSetError, watch, }; } //# sourceMappingURL=useForm.js.map