← Back to all snippets
JAVASCRIPT

Managing Complex Form State with useReducer

Simplify complex form state logic in React by using the useReducer hook. Ideal for forms with multiple fields and intricate validation or submission flows.

import React, { useReducer } from 'react';

const initialFormState = {
  username: '',
  email: '',
  password: '',
  errors: {}
};

function formReducer(state, action) {
  switch (action.type) {
    case 'FIELD_CHANGE':
      return {
        ...state,
        [action.field]: action.value,
        errors: { ...state.errors, [action.field]: undefined } // Clear error on change
      };
    case 'SET_ERRORS':
      return {
        ...state,
        errors: { ...state.errors, ...action.errors }
      };
    case 'RESET_FORM':
      return initialFormState;
    default:
      return state;
  }
}

function SignupForm() {
  const [formState, dispatch] = useReducer(formReducer, initialFormState);
  const { username, email, password, errors } = formState;

  const handleChange = (e) => {
    dispatch({
      type: 'FIELD_CHANGE',
      field: e.target.name,
      value: e.target.value
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const newErrors = {};
    if (!username) newErrors.username = 'Username is required.';
    if (!email) newErrors.email = 'Email is required.';
    if (!password || password.length < 6) newErrors.password = 'Password must be at least 6 characters.';

    if (Object.keys(newErrors).length > 0) {
      dispatch({ type: 'SET_ERRORS', errors: newErrors });
    } else {
      // Simulate API call
      console.log('Form Submitted:', { username, email });
      alert('Form Submitted Successfully!');
      dispatch({ type: 'RESET_FORM' });
    }
  };

  return (
    <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '10px', maxWidth: '300px', margin: '20px auto', padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
      <div>
        <label>
          Username:
          <input type="text" name="username" value={username} onChange={handleChange} />
        </label>
        {errors.username && <p style={{ color: 'red', fontSize: '0.8em' }}>{errors.username}</p>}
      </div>
      <div>
        <label>
          Email:
          <input type="email" name="email" value={email} onChange={handleChange} />
        </label>
        {errors.email && <p style={{ color: 'red', fontSize: '0.8em' }}>{errors.email}</p>}
      </div>
      <div>
        <label>
          Password:
          <input type="password" name="password" value={password} onChange={handleChange} />
        </label>
        {errors.password && <p style={{ color: 'red', fontSize: '0.8em' }}>{errors.password}</p>}
      </div>
      <button type="submit">Sign Up</button>
      <button type="button" onClick={() => dispatch({ type: 'RESET_FORM' })}>Reset</button>
    </form>
  );
}
// To use, render <SignupForm /> in your App component.
How it works: This snippet demonstrates using the `useReducer` hook for managing the state of a complex form. `useReducer` is ideal when state logic involves multiple sub-values or when the next state depends on the previous one. The `formReducer` handles different actions (like `FIELD_CHANGE`, `SET_ERRORS`, `RESET_FORM`), providing a clear and centralized way to update the form state and associated errors, making the form component cleaner and more maintainable.

Need help integrating this into your project?

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

Hire DigitalCodeLabs