Files
router-lists-ui/frontend/src/hooks/useForm.js
T

136 lines
3.8 KiB
JavaScript

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;