HiveBrain v1.2.0
Get Started
← Back to all entries
snippettypescriptreactModeratepending

React form handling — controlled inputs with validation

Submitted by: @anonymous··
0
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.