import { useState } from 'react';
/**
* Custom React hook for managing form state, including values, errors, and touched status.
* It supports basic validation based on a provided schema.
* @param {object} initialValues - The initial values for the form fields.
* @param {object} validationSchema - An optional schema for validating form fields. Each key is a field name, and the value is an object of validation rules (e.g., required, minLength, email, phone).
**/
export const useForm = (initialValues, validationSchema) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
let newValue = type === 'checkbox' ? checked : value;
// Cast select value for role to integer
if (name === 'role' && type === 'select-one') {
newValue = value === '' ? '' : parseInt(value, 10);
}
setValues(prev => ({
...prev,
[name]: newValue
}));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({
...prev,
[name]: true
}));
};
/**
* Validates the current form values against the provided validation schema.
* Updates the errors state and returns true if the form is valid, false otherwise.
*/
const validate = () => {
if (!validationSchema) return true;
const newErrors = {};
Object.keys(validationSchema).forEach(field => {
const rules = validationSchema[field];
const value = values[field];
if (rules.required && (!value || value.toString().trim() === '')) {
newErrors[field] = `${field} is required`;
return;
}
if (rules.minLength && value && value.length < rules.minLength) {
newErrors[field] = `${field} must be at least ${rules.minLength} characters`;
return;
}
if (rules.email && value && !/\S+@\S+\.\S+/.test(value)) {
newErrors[field] = 'Please enter a valid email address';
return;
}
if (rules.phone && value && !/^\+?[\d\s-()]+$/.test(value)) {
newErrors[field] = 'Please enter a valid phone number';
return;
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
/**
* Resets the form state back to the initial values, clearing errors and touched status.
*/
const reset = () => {
setValues(initialValues);
setErrors({});
setTouched({});
};
return {
values,
/** The current values of the form fields. */
errors,
/** An object containing validation errors for each field. */
touched,
/** An object indicating whether each field has been touched (blurred). */
handleChange,
/**
* Handler function for input changes. Updates the values state and clears related errors.
* @param {object} e - The change event object.
*/
handleBlur,
/** Handler function for input blur events. Updates the touched state. */
validate,
/** Function to trigger form validation. */
reset,
setValues
};
};
Source