Homepage

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