react-simple-form-hook
Version:
A lightweight, type-safe React form management hook with built-in Zod validation, field-level validation, touched state tracking, async submissions, and advanced form manipulation features
183 lines (179 loc) • 5.58 kB
JavaScript
;
var react = require('react');
var zod = require('zod');
// src/useForm.ts
function useForm({ initialValues, schema, validateOnChange = false, validateOnBlur = true, onSubmit, }) {
const [values, setValuesState] = react.useState(initialValues);
const [errors, setErrors] = react.useState({});
const [touched, setTouched] = react.useState({});
const [isSubmitting, setIsSubmitting] = react.useState(false);
// Check if form is dirty (has been modified)
const isDirty = react.useMemo(() => {
return JSON.stringify(values) !== JSON.stringify(initialValues);
}, [values, initialValues]);
// Check if form is valid
const isValid = react.useMemo(() => {
try {
schema.parse(values);
return true;
}
catch {
return false;
}
}, [values, schema]);
const handleChange = react.useCallback((event) => {
const { name, value, type } = event.target;
const inputValue = type === 'checkbox' ? event.target.checked : value;
setValuesState((prev) => ({
...prev,
[name]: inputValue,
}));
// Clear error for the field being edited
setErrors((prev) => ({
...prev,
[name]: undefined,
}));
// Validate on change if enabled
if (validateOnChange) {
setTimeout(() => validateField(name), 0);
}
}, [validateOnChange]);
const handleBlur = react.useCallback((event) => {
const { name } = event.target;
setTouched((prev) => ({
...prev,
[name]: true,
}));
// Validate on blur if enabled
if (validateOnBlur) {
validateField(name);
}
}, [validateOnBlur, values]);
const validate = react.useCallback((data) => {
try {
schema.parse(data);
setErrors({});
return true;
}
catch (err) {
if (err instanceof zod.ZodError) {
const fieldErrors = {};
err.issues.forEach((issue) => {
const key = issue.path[0];
fieldErrors[key] = issue.message;
});
setErrors(fieldErrors);
return false;
}
return false;
}
}, [schema]);
const validateField = react.useCallback((field) => {
try {
// Validate the entire object but only set error for this field
schema.parse(values);
setErrors((prev) => ({
...prev,
[field]: undefined,
}));
return true;
}
catch (err) {
if (err instanceof zod.ZodError) {
const fieldError = err.issues.find(issue => issue.path[0] === field);
setErrors((prev) => ({
...prev,
[field]: fieldError?.message,
}));
return !fieldError;
}
return false;
}
}, [values, schema]);
const handleSubmit = react.useCallback((callback) => async (event) => {
event.preventDefault();
// Mark all fields as touched
const allTouched = Object.keys(values).reduce((acc, key) => {
acc[key] = true;
return acc;
}, {});
setTouched(allTouched);
if (validate(values)) {
setIsSubmitting(true);
try {
const submitCallback = callback || onSubmit;
if (submitCallback) {
await submitCallback(values);
}
}
catch (error) {
console.error('Form submission error:', error);
}
finally {
setIsSubmitting(false);
}
}
}, [values, validate, onSubmit]);
const reset = react.useCallback((newValues) => {
setValuesState(newValues || initialValues);
setErrors({});
setTouched({});
setIsSubmitting(false);
}, [initialValues]);
const setFieldValue = react.useCallback((field, value) => {
setValuesState((prev) => ({
...prev,
[field]: value,
}));
}, []);
const setFieldError = react.useCallback((field, error) => {
setErrors((prev) => ({
...prev,
[field]: error,
}));
}, []);
const setFieldTouched = react.useCallback((field, isTouched) => {
setTouched((prev) => ({
...prev,
[field]: isTouched,
}));
}, []);
const clearErrors = react.useCallback(() => {
setErrors({});
}, []);
const setValues = react.useCallback((newValues) => {
setValuesState((prev) => ({
...prev,
...newValues,
}));
}, []);
const getFieldProps = react.useCallback((field) => {
return {
name: field,
value: values[field],
onChange: handleChange,
onBlur: handleBlur,
};
}, [values, handleChange, handleBlur]);
return {
values,
errors,
touched,
isDirty,
isSubmitting,
isValid,
handleChange,
handleBlur,
handleSubmit,
setFieldValue,
setFieldError,
setFieldTouched,
validateField,
reset,
clearErrors,
setValues,
getFieldProps,
};
}
exports.useForm = useForm;
//# sourceMappingURL=index.js.map