typescript 83 lines · 1 tab

React useReducer for complex state logic

Maya Patel Jan 2026
1 tab
import { useReducer } from 'react'

interface FormState {
  values: Record<string, any>
  errors: Record<string, string>
  touched: Record<string, boolean>
  isSubmitting: boolean
}

type FormAction =
  | { type: 'CHANGE_FIELD'; field: string; value: any }
  | { type: 'TOUCH_FIELD'; field: string }
  | { type: 'SET_ERRORS'; errors: Record<string, string> }
  | { type: 'SET_SUBMITTING'; isSubmitting: boolean }
  | { type: 'RESET' }

function formReducer(state: FormState, action: FormAction): FormState {
  switch (action.type) {
    case 'CHANGE_FIELD':
      return {
        ...state,
        values: {
          ...state.values,
          [action.field]: action.value,
        },
        errors: {
          ...state.errors,
          [action.field]: '', // Clear error on change
        },
      }

    case 'TOUCH_FIELD':
      return {
        ...state,
        touched: {
          ...state.touched,
          [action.field]: true,
        },
      }

    case 'SET_ERRORS':
      return {
        ...state,
        errors: action.errors,
      }

    case 'SET_SUBMITTING':
      return {
        ...state,
        isSubmitting: action.isSubmitting,
      }

    case 'RESET':
      return initialState

    default:
      return state
  }
}

const initialState: FormState = {
  values: {},
  errors: {},
  touched: {},
  isSubmitting: false,
}

export function useFormReducer() {
  const [state, dispatch] = useReducer(formReducer, initialState)

  return {
      state,
    changeField: (field: string, value: any) =>
      dispatch({ type: 'CHANGE_FIELD', field, value }),
    touchField: (field: string) =>
      dispatch({ type: 'TOUCH_FIELD', field }),
    setErrors: (errors: Record<string, string>) =>
      dispatch({ type: 'SET_ERRORS', errors }),
    setSubmitting: (isSubmitting: boolean) =>
      dispatch({ type: 'SET_SUBMITTING', isSubmitting }),
    reset: () => dispatch({ type: 'RESET' }),
  }
}
1 file · typescript Explain with highlit

useReducer manages state with reducer patterns like Redux but locally scoped. When state updates depend on previous state or involve multiple sub-values, reducers are clearer than multiple useState calls. The reducer function takes current state and an action, returning new state. Actions are objects with type and optional payload. This pattern centralizes state logic, making it easier to test and reason about. I use useReducer for complex forms, wizards, or mini state machines. Combined with Context, it provides Redux-like architecture without external dependencies. TypeScript discriminated unions ensure type-safe action handling.


Related snips

typescript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
  timeout: 15000,

Axios API client with interceptors

react axios api
by Maya Patel 1 tab
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface FilterState {
  search: string
  category: string | null

Zustand for lightweight state management

react zustand state-management
by Maya Patel 2 tabs
swift
import SwiftUI

struct ContentView: View {
    @State private var username = ""
    @State private var isLoggedIn = false
    @StateObject private var viewModel = LoginViewModel()

SwiftUI declarative UI with state management

swift swiftui ios
by Sofia Martinez 2 tabs
typescript
import React from "react";

type FallbackProps = {
  error: Error;
  reset: () => void;
};

React Error Boundary + error reporting hook

react frontend error-boundary
by codesnips 3 tabs
javascript
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
  openAnalyzer: true,
});

/** @type {import('next').NextConfig} */

Next.js bundle analyzer for targeted performance work

nextjs performance tooling
by codesnips 4 tabs
typescript
import { Navigate, useLocation } from 'react-router-dom'
import { useAuth } from '@/contexts/AuthContext'

interface ProtectedRouteProps {
  children: React.ReactNode
}

React Router with protected routes

react react-router routing
by Maya Patel 2 tabs

Share this code

Here's the card — post it anywhere.

React useReducer for complex state logic — share card
Link copied