Tutorials Logic, IN info@tutorialslogic.com
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Website Development
Practice
Quiz Challenge Interview Questions Certification Practice
Tools
Online Compiler JSON Formatter Regex Tester CSS Unit Converter Color Picker
Compiler Tools

React useReducer State Management Pattern: Tutorial, Examples, FAQs & Interview Tips

What Is useReducer?

useReducer is a React hook used to manage state when update logic becomes more structured or complex. Instead of updating values directly with a setter, you dispatch actions and let a reducer function decide how the next state should look.

This pattern is helpful when many actions can affect the same state, when several state fields belong together, or when you want state transitions to be easier to read and test.

useState vs useReducer

FeatureuseStateuseReducer
Best forSimple independent valuesComplex or related state
Update styleCall setter directlyDispatch actions
Logic locationUsually inside component handlersCentralized in reducer function
ReadabilityGreat for simple casesBetter when many transitions exist

How a Reducer Works

A reducer is a function that receives the current state and an action object, then returns the next state. The component does not directly change values. It only dispatches actions such as increment, reset, or remove.

Basic Example

Counter with useReducer
function counterReducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 }
        case 'decrement':
            return { count: state.count - 1 }
        case 'reset':
            return { count: 0 }
        default:
            return state
    }
}
import { useReducer } from 'react'

function counterReducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 }
        case 'decrement':
            return { count: state.count - 1 }
        case 'reset':
            return { count: 0 }
        default:
            return state
    }
}

function Counter() {
    const [state, dispatch] = useReducer(counterReducer, { count: 0 })

    return (
        <div>
            <p>Count: {state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>+</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
            <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
        </div>
    )
}

Why useReducer Is Useful

  • It keeps update logic in one place
  • It works well when many actions affect the same state
  • It makes transitions easier to read in larger components
  • Reducer functions are easier to test because they are plain functions

Todo Example

Todo App with useReducer
import { useReducer, useState } from 'react'

function todoReducer(state, action) {
    switch (action.type) {
        case 'add':
            return [...state, { id: Date.now(), text: action.text }]
        case 'remove':
            return state.filter(todo => todo.id !== action.id)
        default:
            return state
    }
}

function TodoApp() {
    const [todos, dispatch] = useReducer(todoReducer, [])
    const [input, setInput] = useState('')

    function addTodo() {
        if (!input.trim()) return
        dispatch({ type: 'add', text: input })
        setInput('')
    }

    return (
        <div>
            <input value={input} onChange={e => setInput(e.target.value)} placeholder="New task" />
            <button onClick={addTodo}>Add</button>
            <ul>
                {todos.map(todo => (
                    <li key={todo.id}>
                        {todo.text}
                        <button onClick={() => dispatch({ type: 'remove', id: todo.id })}>Remove</button>
                    </li>
                ))}
            </ul>
        </div>
    )
}

Form State Example

Reducers are also useful when a form has several related fields and multiple possible actions such as update, reset, and submit.

import { useReducer } from 'react'

function formReducer(state, action) {
    switch (action.type) {
        case 'update_field':
            return {
                ...state,
                [action.field]: action.value
            }
        case 'reset':
            return { name: '', email: '' }
        default:
            return state
    }
}

When to Use useReducer

  • Use useState for simple values like a toggle or one input field
  • Use useReducer when many actions affect the same state
  • Use useReducer when multiple fields belong together
  • Use useReducer when state transitions should be explicit and testable

Common Mistakes

MistakeWhy it is a problemBetter approach
Using useReducer for very simple stateAdds extra ceremony with little benefitUse useState for simple cases
Writing reducers that mutate stateBreaks predictable updatesReturn a new state object or array
Using unclear action namesMakes reducer logic hard to followUse descriptive action types like add_todo or reset_form
Placing too much unrelated logic inside one reducerCreates a large hard-to-maintain reducerKeep reducers focused or split responsibilities

Best Practices

  • Use descriptive action objects
  • Keep reducers pure and predictable
  • Return new objects and arrays instead of mutating state
  • Choose useReducer when it improves clarity, not by default
  • Test reducer functions independently when the logic becomes important

Summary

useReducer is a strong choice when component state has multiple transitions or related fields. It centralizes state logic, makes updates easier to reason about, and scales better than scattered state handlers when the logic becomes more complex.

Key Takeaways
  • useReducer is useful when state logic becomes more complex than a simple useState setup.
  • A reducer receives the current state and an action, then returns the next state.
  • dispatch() sends actions to the reducer.
  • useReducer works well for forms, lists, and multi-step state transitions.
  • Reducers should stay pure and return new state values instead of mutating old ones.
  • Use useState for simple cases and useReducer when it makes updates clearer.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.