DEV Community

Cover image for Using useReducer for Complex State Logic
Reme Le Hane
Reme Le Hane

Posted on • Originally published at open.substack.com

Using useReducer for Complex State Logic

Skill: Manage Complex State with useReducer

Instead of juggling multiple useState calls, you can use useReducer to handle state transitions in a more structured way, similar to how Redux works.

When to Use useReducer

  • When your state has multiple interdependent variables (e.g., a form or a complex UI).

  • When your state transitions are complex and involve multiple actions.

  • When you want a cleaner and more scalable state management solution.

Example: Managing a Complex Form

Let’s use useReducer to handle the state of a form with multiple fields and actions:

import React, { useReducer } from "react"; // Define initial state const initialState = { username: "", email: "", password: "", isSubmitting: false, error: null, }; // Define a reducer function const formReducer = (state, action) => { switch (action.type) { case "SET_FIELD": return { ...state, [action.field]: action.value }; case "SUBMIT_START": return { ...state, isSubmitting: true, error: null }; case "SUBMIT_SUCCESS": return { ...initialState }; // Reset form on success case "SUBMIT_ERROR": return { ...state, isSubmitting: false, error: action.error }; default: throw new Error(`Unknown action type: ${action.type}`); } }; const ComplexForm = () => { // Use useReducer to manage form state const [state, dispatch] = useReducer(formReducer, initialState); const handleChange = (e) => { const { name, value } = e.target; dispatch({ type: "SET_FIELD", field: name, value }); }; const handleSubmit = async (e) => { e.preventDefault(); dispatch({ type: "SUBMIT_START" }); try { // Simulate form submission await new Promise((resolve) => setTimeout(resolve, 1000)); console.log("Form submitted:", state); dispatch({ type: "SUBMIT_SUCCESS" }); } catch (error) { dispatch({ type: "SUBMIT_ERROR", error: "Submission failed!" }); } }; return ( <form onSubmit={handleSubmit}> <div> <label>Username:</label>  <input name="username" value={state.username} onChange={handleChange} />  </div>  <div> <label>Email:</label>  <input name="email" value={state.email} onChange={handleChange} />  </div>  <div> <label>Password:</label>  <input name="password" type="password" value={state.password} onChange={handleChange} />  </div>  {state.error && <p style={{ color: "red" }}>{state.error}</p>}  <button type="submit" disabled={state.isSubmitting}> {state.isSubmitting ? "Submitting..." : "Submit"} </button>  </form>  ); }; export default ComplexForm; 
Enter fullscreen mode Exit fullscreen mode

How It Works

  1. State Centralization:
  • All form fields, submission state, and error handling are managed in a single state object.
  1. Reducer Logic:
  • Actions (SET_FIELD, SUBMIT_START, etc.) describe what happens.

  • The reducer updates the state based on these actions.

  1. Form Submission:
  • On form submission, the state transitions smoothly through SUBMIT_START, SUBMIT_SUCCESS, or SUBMIT_ERROR.

Why Use This?

  • Scalability: Adding more fields or actions (like reset) is straightforward.

  • Readability: State transitions are centralized and predictable.

  • Performance: State updates are handled in bulk, reducing re-renders compared to multiple useState calls.

This skill is invaluable for managing complex state logic and making your React components more robust!

Top comments (0)