How to use the useReducer hook in React
Updating state with the useReducer hook.
When handling state in your react components you might often reach for the
useState
hook. That is handy for simple state values that only pertain to that
component, but when you need to deal with complex state objects that can be used
and updated across multiple components within your application, the useReducer
hook can come in handy.
TL;DR – The
useReducer
hook is for setting and managing state logic that is too complex for theuseState
hook. It accepts a reducer function to handle state updates.
When to use useReducer over useState?
- When you are dealing with complex state objects
- If multiple components can update state
- If next state depends on previous state
- Your overall react app architecture is complex
Basic usage of useReducer
The useReducer hook takes a reducer function and some initial state and returns an array with a state variable and dispatch function. In comparison to setState the state variable is the current state and the dispatch function is used to set state. The big difference is that setState in the useState hook takes the new state whereas the dispatch acception an action.
What is a reducer action?
A reducer action is a JavaScript object that describes the state change. A
type
property is required on all actions as this is what will tell the reducer
how to update state (this is conventionally written as an uppercase string). An
optional payload
property is used to send some additional data that pertains
to the state change.
A reducer action is a JavaScript object that describes the state change.
Putting this all together we can write a simple component that uses useReducer
for setting the first name:
const initialState = { firstName: "" }
const reducerFn = (state, action) => {
switch (action.type) {
case "SET_FIRST_NAME":
return {
...state,
firstName: action.payload,
}
default:
return state
}
}
const Component = () => {
const [state, dispatch] = useReducer(reducerFn, initialState)
return (
<div>
<div>{state.firstName}</div>
<button
onClick={() => dispatch({ type: "SET_FIRST_NAME", payload: "Dwight" })}
>
Set name
</button>
</div>
)
}
Does useReducer replace Redux?
The useReducer hook by itself does not replace Redux because it does not create a global store. You can have multiple state objects in your react application that are isolated from each other, which violates Redux's single source of true principle.
Combining useReducer
with React.Context
can get you closer to replicating
most of Redux’s functionality because you can store all of your state in a
global context.
Using useReducer with React.Context
Combining React.Context
with useReducer
allows you to have global state with
reducer state updates. This is a great alternative to Redux that’s built right
into React.
import { createContext, useReducer, useContext } from "react"
const todosDefaultState = {
todos: [],
}
function todosReducer(state = todosDefaultState, action = {}) {
switch (action.type) {
case "NEW_TODO":
return { ...state, todos: [...state.todos, action.payload] }
case "COMPLETE_TODO":
const updatedTodos = state.todos.map((todo) => {
if (todo.id === action.id) {
return { ...todo, done: true }
} else {
return todo
}
})
return { ...state, todos: updatedTodos }
default:
return state
}
}
const StoreContext = createContext(null)
export function StoreProvider({ children }) {
const [todosState, todosDispatch] = useReducer(
todosReducer,
todosDefaultState
)
return (
<StoreContext.Provider value={{ todosState, todosDispatch }}>
{children}
</StoreContext.Provider>
)
}
export const useStore = () => useContext(StoreContext)
You can wrap your entire application with the StoreContext.Provider
component
and have direct access to it's state and dispatch functions.
Have some feedback on this post? .