useForm
A hook for form state management and validation. This custom hook provides a comprehensive solution for handling form state, validation, and submission in React applications.
Implementation
function useForm<T extends Record<string, any>>(
initialValues: T,
validate?: (values: T) => Partial<Record<keyof T, string>>
) {
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({});
const handleChange = (name: keyof T, value: any) => {
setValues(prev => ({ ...prev, [name]: value }));
if (validate) {
const validationErrors = validate({ ...values, [name]: value });
setErrors(prev => ({ ...prev, [name]: validationErrors[name] }));
}
};
const handleBlur = (name: keyof T) => {
setTouched(prev => ({ ...prev, [name]: true }));
};
const handleSubmit = (onSubmit: (values: T) => void) => (e: React.FormEvent) => {
e.preventDefault();
if (validate) {
const validationErrors = validate(values);
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
onSubmit(values);
}
} else {
onSubmit(values);
}
};
return {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
};
}
Usage
Simple form with validation
export default function App() {
const validate = (values) => {
const errors = {};
if (values.username.length < 3) {
errors.username = 'Too short';
}
if (!values.email.includes('@')) {
errors.email = 'Invalid email';
}
return errors;
};
const { values, errors, handleChange, handleSubmit } = useForm(
{
username: '',
email: ''
},
validate
);
return (
<form onSubmit={handleSubmit((values)=> alert(JSON.stringify(values, null, 2)))}>
<div style={{ marginBottom: '10px' }}>
<input
placeholder="Username"
value={values.username}
onChange={(e)=> handleChange('username', e.target.value)}
/>
{errors.username && (
<div style={{ color: 'red', fontSize: '12px' }}>{errors.username}</div>
)}
</div>
<div style={{ marginBottom: '10px' }}>
<input
placeholder="Email"
value={values.email}
onChange={(e)=> handleChange('email', e.target.value)}
/>
{errors.email && (
<div style={{ color: 'red', fontSize: '12px' }}>{errors.email}</div>
)}
</div>
<button type="submit">Sign Up</button>
</form>
);
}
Parameters
initialValuesTrequired
Initial form values
validate(values: T) => Partial<Record<keyof T, string>>
Validation function
Live Playground
Best Practices
- Define validation schema upfront
- Handle all form fields consistently
- Use with TypeScript for type safety
Performance
- Efficient form state updates
- Optimized validation timing
- Minimal re-renders