JAVASCRIPT

Simplify Form State & Validation with useForm

A robust React hook for managing complex form states, values, and errors, providing a streamlined API for handling user input and custom validation logic.

import { useState, useCallback, useMemo } from 'react';

const useForm = (initialValues = {}, validate = () => ({})) => {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const handleChange = useCallback((event) => {
    event.persist(); 
    setValues(prevValues => ({
      ...prevValues,
      [event.target.name]: event.target.value,
    }));
    
    if (errors[event.target.name]) {
      setErrors(prevErrors => {
        const newErrors = { ...prevErrors };
        delete newErrors[event.target.name];
        return newErrors;
      });
    }
  }, [errors]); 

  const handleSubmit = useCallback(async (callback) => {
    setIsSubmitting(true);
    const validationErrors = await Promise.resolve(validate(values)); 
    setErrors(validationErrors || {});

    if (Object.keys(validationErrors || {}).length === 0) {
      await Promise.resolve(callback(values)); 
    }
    setIsSubmitting(false);
  }, [values, validate]);

  const resetForm = useCallback(() => {
    setValues(initialValues);
    setErrors({});
    setIsSubmitting(false);
  }, [initialValues]);

  const formData = useMemo(() => ({
    values,
    errors,
    handleChange,
    handleSubmit,
    resetForm,
    isSubmitting,
  }), [values, errors, handleChange, handleSubmit, resetForm, isSubmitting]);

  return formData;
};

export default useForm;

/*
// Example Usage:
const validateForm = (values) => {
  const newErrors = {};
  if (!values.username) {
    newErrors.username = 'Username is required';
  }
  if (!values.email) {
    newErrors.email = 'Email is required';
  } else if (!/\S+@\S+\.\S+/.test(values.email)) {
    newErrors.email = 'Email address is invalid';
  }
  return newErrors;
};

function LoginForm() {
  const { values, errors, handleChange, handleSubmit, isSubmitting, resetForm } = useForm(
    { username: '', email: '', password: '' },
    validateForm
  );

  const onSubmit = (formValues) => {
    alert(JSON.stringify(formValues, null, 2));
    console.log('Form submitted:', formValues);
    return new Promise(resolve => setTimeout(resolve, 1000)); // Simulate API call
  };

  return (
    <form onSubmit={(e) => { e.preventDefault(); handleSubmit(onSubmit); }}>
      <div>
        <label>Username:</label>
        <input
          type="text"
          name="username"
          value={values.username || ''}
          onChange={handleChange}
        />
        {errors.username && <p style={{ color: 'red' }}>{errors.username}</p>}
      </div>
      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          value={values.email || ''}
          onChange={handleChange}
        />
        {errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}
      </div>
      <div>
        <label>Password:</label>
        <input
          type="password"
          name="password"
          value={values.password || ''}
          onChange={handleChange}
        />
      </div>
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Submitting...' : 'Submit'}
      </button>
      <button type="button" onClick={resetForm}>Reset</button>
    </form>
  );
}
*/
How it works: The `useForm` hook simplifies managing complex form states, including input values, validation errors, and submission status. It takes `initialValues` and an optional `validate` function (which can be synchronous or asynchronous) that returns an errors object. `handleChange` updates input values and clears associated errors. `handleSubmit` runs the validation, updates error states, and only calls the provided callback if there are no errors, also managing a `isSubmitting` state. The `resetForm` function reverts the form to its initial state.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs