import { useState, useCallback } from 'react'; /** * useForm - hook для управления формами с валидацией * * @param {Object} initialValues - начальные значения полей * @param {Object} validationSchema - схема валидации {fieldName: validatorFn} * @returns {Object} - {values, errors, touched, isSubmitting, handlers} * * Пример использования: * const { values, errors, handleChange, handleBlur, handleSubmit } = useForm( * { email: '', password: '' }, * { * email: (value) => !value ? 'Email обязателен' : null, * password: (value) => value.length < 6 ? 'Минимум 6 символов' : null * } * ); */ function useForm(initialValues, validationSchema = {}) { const [values, setValues] = useState(initialValues); const [errors, setErrors] = useState({}); const [touched, setTouched] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); // Валидация одного поля const validateField = useCallback((name, value) => { if (!validationSchema[name]) return null; const validator = validationSchema[name]; const error = validator(value, values); return error; }, [validationSchema, values]); // Обработчик изменения поля const handleChange = useCallback((name, value) => { setValues(prev => ({ ...prev, [name]: value })); // Валидация при изменении, если поле уже было touched if (touched[name]) { const error = validateField(name, value); setErrors(prev => ({ ...prev, [name]: error })); } }, [touched, validateField]); // Обработчик blur (потеря фокуса) const handleBlur = useCallback((name) => { setTouched(prev => ({ ...prev, [name]: true })); const value = values[name]; const error = validateField(name, value); setErrors(prev => ({ ...prev, [name]: error })); }, [values, validateField]); // Валидация всех полей const validate = useCallback(() => { const newErrors = {}; let isValid = true; Object.keys(validationSchema).forEach(name => { const error = validateField(name, values[name]); if (error) { newErrors[name] = error; isValid = false; } }); setErrors(newErrors); return isValid; }, [validationSchema, values, validateField]); // Обработчик submit const handleSubmit = useCallback(async (onSubmit) => { setIsSubmitting(true); // Отмечаем все поля как touched const allTouched = Object.keys(values).reduce((acc, key) => { acc[key] = true; return acc; }, {}); setTouched(allTouched); const isValid = validate(); if (isValid) { try { await onSubmit(values); } catch (error) { console.error('Form submission error:', error); throw error; } finally { setIsSubmitting(false); } } else { setIsSubmitting(false); } }, [values, validate]); // Сброс формы const reset = useCallback(() => { setValues(initialValues); setErrors({}); setTouched({}); setIsSubmitting(false); }, [initialValues]); // Установка значения поля программно const setValue = useCallback((name, value) => { setValues(prev => ({ ...prev, [name]: value })); }, []); // Установка ошибки поля программно const setError = useCallback((name, error) => { setErrors(prev => ({ ...prev, [name]: error })); }, []); return { values, errors, touched, isSubmitting, handleChange, handleBlur, handleSubmit, validate, reset, setValue, setError, setValues, setErrors }; } export default useForm;