react-vite-themes
Version:
A test/experimental React theme system created for learning purposes. Features atomic design components, SCSS variables, and dark/light theme support. Not intended for production use.
169 lines (168 loc) • 6.01 kB
JavaScript
import { useState, useCallback, useMemo } from 'react';
import { validateField, validateForm, isFormValid } from '../utils/validation';
export const useForm = (options = {}) => {
const { initialValues = {}, validationSchema = {}, onSubmit, validateOnChange = false, validateOnBlur = true } = options;
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
// Validate a single field
const validateFieldValue = useCallback((field, value) => {
const rules = validationSchema[field];
if (!rules) {
return { isValid: true, errors: [] };
}
return validateField(value, rules);
}, [validationSchema]);
// Validate entire form
const validateFormValues = useCallback(() => {
const validationResults = validateForm(values, validationSchema);
const newErrors = {};
Object.entries(validationResults).forEach(([field, result]) => {
if (!result.isValid) {
newErrors[field] = result.errors;
}
});
setErrors(newErrors);
return isFormValid(validationResults);
}, [values, validationSchema]);
// Set a single field value
const setValue = useCallback((field, value) => {
setValues(prev => ({ ...prev, [field]: value }));
if (validateOnChange) {
const result = validateFieldValue(field, value);
setErrors(prev => ({
...prev,
[field]: result.errors
}));
}
}, [validateOnChange, validateFieldValue]);
// Set multiple field values
const setValuesHandler = useCallback((newValues) => {
setValues(newValues);
}, []);
// Set field error
const setFieldError = useCallback((field, error) => {
setErrors(prev => ({
...prev,
[field]: [error]
}));
}, []);
// Set multiple field errors
const setFieldErrors = useCallback((newErrors) => {
setErrors(newErrors);
}, []);
// Validate a single field
const validateFieldHandler = useCallback((field) => {
const result = validateFieldValue(field, values[field]);
setErrors(prev => ({
...prev,
[field]: result.errors
}));
}, [validateFieldValue, values]);
// Validate entire form
const validateFormHandler = useCallback(() => {
return validateFormValues();
}, [validateFormValues]);
// Handle field blur
const handleBlur = useCallback((field) => {
setTouched(prev => ({ ...prev, [field]: true }));
if (validateOnBlur) {
validateFieldHandler(field);
}
}, [validateOnBlur, validateFieldHandler]);
// Handle field change
const handleChange = useCallback((field, value) => {
setValue(field, value);
}, [setValue]);
// Handle form submission
const handleSubmit = useCallback(async (e) => {
if (e) {
e.preventDefault();
}
// Mark all fields as touched
const allTouched = {};
Object.keys(validationSchema).forEach(field => {
allTouched[field] = true;
});
setTouched(allTouched);
// Validate form
const isValid = validateFormHandler();
if (isValid && onSubmit) {
setIsSubmitting(true);
try {
const result = onSubmit(values);
// If onSubmit returns a Promise, wait for it
if (result instanceof Promise) {
await result;
}
}
finally {
setIsSubmitting(false);
}
}
}, [validationSchema, validateFormHandler, onSubmit, values]);
// Reset form
const reset = useCallback(() => {
setValues(initialValues);
setErrors({});
setTouched({});
setIsSubmitting(false);
}, [initialValues]);
// Reset errors
const resetErrors = useCallback(() => {
setErrors({});
}, []);
// Get field props for easy integration with inputs
const getFieldProps = useCallback((field) => {
const fieldValue = values[field];
let displayValue = '';
// Convert value to string for display
if (fieldValue !== undefined && fieldValue !== null) {
if (typeof fieldValue === 'string' || typeof fieldValue === 'number') {
displayValue = String(fieldValue);
}
else if (fieldValue instanceof Date) {
displayValue = fieldValue.toISOString().split('T')[0];
}
else {
displayValue = String(fieldValue);
}
}
return {
value: displayValue,
onChange: (value) => handleChange(field, value),
onBlur: () => handleBlur(field),
error: errors[field] || [],
isInvalid: errors[field] && errors[field].length > 0,
isTouched: touched[field] || false
};
}, [values, errors, touched, handleChange, handleBlur]);
// Check if form is valid
const isValid = useMemo(() => {
return Object.keys(validationSchema).length === 0 ||
Object.keys(validationSchema).every(field => {
const result = validateFieldValue(field, values[field]);
return result.isValid;
});
}, [validationSchema, values, validateFieldValue]);
return {
values,
errors,
touched,
isValid,
isSubmitting,
setValue,
setValues: setValuesHandler,
setFieldError,
setFieldErrors,
validateField: validateFieldHandler,
validateForm: validateFormHandler,
handleSubmit,
handleBlur,
handleChange,
reset,
resetErrors,
getFieldProps
};
};