Forms are deceptively deep: you need current values, per-field errors from validation, which fields have been touched (so you don't show "Required" before the user has even reached the field), and an isSubmitting flag to disable the button during an async submit. useForm is a minimal version of the Formik/React-Hook-Form core — one hook that owns all four and wires up handleChange/handleBlur/handleSubmit.
Implement useForm({ initialValues, validate, onSubmit }). Return { values, errors, touched, isSubmitting, handleChange, handleBlur, handleSubmit, setFieldValue, reset }. Validate on change; on submit, validate everything, mark all fields touched, and only call onSubmit when there are no errors.
function useForm({ initialValues, validate, onSubmit }) {
// returns { values, errors, touched, isSubmitting,
// handleChange, handleBlur, handleSubmit, setFieldValue, reset }
}
const f = useForm({
initialValues: { email: '' },
validate: (v) => (v.email.includes('@') ? {} : { email: 'Invalid email' }),
onSubmit: (v) => api.subscribe(v),
});
<form onSubmit={f.handleSubmit}>
<input name="email" value={f.values.email} onChange={f.handleChange} onBlur={f.handleBlur} />
{f.touched.email && f.errors.email && <span>{f.errors.email}</span>}
<button disabled={f.isSubmitting}>Subscribe</button>
</form>
validate(values) returns { field: message } for invalid fields ({} when valid); run it on change and on submit.handleChange reads e.target (checkbox → checked, else value), updates that field, and re-validates.touched — handleBlur marks a field touched; handleSubmit marks all fields touched (so errors show after a submit attempt).handleSubmit — preventDefault, validate; if there are errors, stop; otherwise set isSubmitting and await onSubmit(values), clearing the flag when done. Keep the handlers stable.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.