snippettypescriptreactModeratepending
React form handling — controlled inputs with validation
Viewed 0 times
useFormform validationcontrolled inputform stateerror handling
browser
Problem
Need a lightweight form handler for React without pulling in Formik or react-hook-form. Need validation, error messages, dirty tracking, and submit handling.
Solution
Minimal custom useForm hook with field registration, validation, and error state. Zero dependencies.
Code Snippets
Minimal useForm hook with validation and field binding
import { useState, FormEvent } from 'react';
type Errors<T> = Partial<Record<keyof T, string>>;
type Validator<T> = (values: T) => Errors<T>;
function useForm<T extends Record<string, any>>(opts: {
initial: T;
validate?: Validator<T>;
onSubmit: (values: T) => void | Promise<void>;
}) {
const [values, setValues] = useState(opts.initial);
const [errors, setErrors] = useState<Errors<T>>({});
const [submitting, setSubmitting] = useState(false);
const field = (name: keyof T) => ({
name,
value: values[name],
onChange: (e: any) => {
const val = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
setValues(prev => ({ ...prev, [name]: val }));
if (errors[name]) setErrors(prev => ({ ...prev, [name]: undefined }));
},
});
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
const errs = opts.validate?.(values) ?? {};
setErrors(errs);
if (Object.keys(errs).length > 0) return;
setSubmitting(true);
try { await opts.onSubmit(values); }
finally { setSubmitting(false); }
};
return { values, errors, submitting, field, handleSubmit, setValues };
}
// Usage
function LoginForm() {
const form = useForm({
initial: { email: '', password: '' },
validate: (v) => ({
...(!v.email && { email: 'Required' }),
...(v.password.length < 8 && { password: 'Min 8 chars' }),
}),
onSubmit: async (v) => { await login(v.email, v.password); },
});
return (
<form onSubmit={form.handleSubmit}>
<input {...form.field('email')} />
{form.errors.email && <span>{form.errors.email}</span>}
<input type="password" {...form.field('password')} />
<button disabled={form.submitting}>Login</button>
</form>
);
}Revisions (0)
No revisions yet.