HomeAbout Me

Advanced React APIs: Advanced State Management

By Daniel Nguyen
Published in React JS
June 11, 2025
1 min read
Advanced React APIs: Advanced State Management

🧠 Mastering useReducer in React: A Practical Deep Dive

React’s useState hook is perfect for many scenarios — it’s simple, concise, and expressive. But as your state grows in complexity, managing it with useState can start to feel messy. That’s where useReducer shines.

This post will walk you through progressively mastering useReducer—starting from simple values like counters, all the way to simulating this.setState behavior, and finally using it for real-world applications like a Tic Tac Toe game.

📦 Why Use useReducer?

React’s useReducer is ideal when:

  • You want to separate state logic from the component
  • Multiple values in your state change together
  • You want more predictable and declarative state updates

Let’s explore useReducer step-by-step with increasingly powerful examples.


🧮 Step 1: From useState to useReducer — Basic Usage

Let’s start by replacing useState with useReducer for something simple: updating an input value.

function nameReducer(previousName: string, newName: string) {
return newName
}
const initialNameValue = 'Joe'
function NameInput() {
const [name, setName] = useReducer(nameReducer, initialNameValue)
const handleChange = (e) => setName(e.currentTarget.value)
return (
<div>
<label>
Name: <input defaultValue={name} onChange={handleChange} />
</label>
<div>You typed: {name}</div>
</div>
)
}

👉 In this case, useReducer behaves just like useState. The reducer simply returns the new value passed via dispatch.


🔁 Step 2: Updating a Counter with a Custom Reducer

Let’s build a counter:

const countReducer = (currentCount: number, action: number) =>
currentCount + action
const initialCount = 0
const step = 1
function Counter() {
const [count, changeCount] = useReducer(countReducer, initialCount)
const increment = () => changeCount(step)
const decrement = () => changeCount(-step)
return (
<div>
<button onClick={decrement}></button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}

🧠 Lesson: The action can be any value — number, string, object, or function — it’s up to you how to use it inside the reducer.


📦 Step 3: Use State Objects (Simulating this.setState)

Now let’s work with objects to group state variables together — much like class components used to do with this.setState.

function countReducer(state, newState) {
return { ...state, ...newState }
}
const initialState = {
count: 0,
someOtherState: 'hello',
}
function Counter() {
const [state, setState] = useReducer(countReducer, initialState)
const { count } = state
const step = 1
const increment = () => setState({ count: count + step })
const decrement = () => setState({ count: count - step })
return (
<div>
<div>{state.someOtherState}</div>
<button onClick={decrement}></button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}

💡 Why this matters: Updating state via object merging makes useReducer behave a lot like this.setState, giving you flexibility while still keeping logic outside your UI code.


🔄 Step 4: Support Function Updaters (Advanced this.setState Simulation)

Let’s make our reducer handle both objects and functions, just like this.setState could.

function countReducer(state, action) {
const newState = typeof action === 'function' ? action(state) : action
return { ...state, ...newState }
}

Now your component can call setState with a function:

const increment = () =>
setState((currentState) => ({ count: currentState.count + step }))

Benefit: This allows state updates based on the previous state — very useful when updates are asynchronous or based on current values.


🧱 Step 5: Traditional Reducer Pattern

Let’s follow the conventional reducer pattern with action types. This is common in Redux and large-scale React apps:

function countReducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + action.step }
case 'DECREMENT':
return { ...state, count: state.count - action.step }
default:
return state
}
}

Then in your component:

const [state, dispatch] = useReducer(countReducer, { count: 0 })
const step = 1
const increment = () => dispatch({ type: 'INCREMENT', step })
const decrement = () => dispatch({ type: 'DECREMENT', step })

🧘 Now your component just dispatches actions — no logic about how state changes is embedded in the UI.


🎮 Step 6: Real World — useReducer in Tic Tac Toe

Let’s go further and use useReducer in a game.

We’ll define our actions like this:

type GameAction =
| { type: 'SELECT_SQUARE'; index: number }
| { type: 'SELECT_STEP'; step: number }
| { type: 'RESTART' }

And the reducer:

function gameReducer(state, action) {
switch (action.type) {
case 'SELECT_SQUARE': {
const { index } = action
// logic to update the board
return newState
}
case 'SELECT_STEP': {
return {
...state,
currentStep: action.step,
}
}
case 'RESTART': {
return getInitialGameState()
}
default:
return state
}
}

Using useReducer:

const [state, dispatch] = useReducer(gameReducer, null, getInitialGameState)

🌀 Lazy initialization: We pass a third argument to useReducer (getInitialGameState) to avoid calculating initial state on every render.


📚 Summary

useReducer is an advanced tool that opens up better patterns for managing complex state. Here’s a quick breakdown of what we’ve learned:

PatternDescription
Basic value reducerLike useState, but abstracted
Object merging like setStateUseful for grouped state values
Function updater supportHandles async-safe updates
Action type patternScales for larger apps
Real-world use (e.g. game)Clean separation of logic and UI

📎 Resources to Dive Deeper

  • 📘 React Docs: useReducer
  • ✍️ Should I useState or useReducer?
  • 🧩 How to Implement useState with useReducer


Tags

#AdvancedReactAPIs

Share

Previous Article
Advanced React APIs: Introduction

Table Of Contents

1
📦 Why Use useReducer?
2
🧮 Step 1: From useState to useReducer — Basic Usage
3
🔁 Step 2: Updating a Counter with a Custom Reducer
4
📦 Step 3: Use State Objects (Simulating this.setState)
5
🔄 Step 4: Support Function Updaters (Advanced this.setState Simulation)
6
🧱 Step 5: Traditional Reducer Pattern
7
🎮 Step 6: Real World — useReducer in Tic Tac Toe
8
📚 Summary
9
📎 Resources to Dive Deeper

Related Posts

Advanced React APIs: Sync Exernal Store
June 19, 2025
2 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media