Actions and reducers in React are the two halves of the useReducer pattern. An action is a plain object that describes what the user did, and a reducer is a pure function that returns the next state from the current state and that action. Naming each piece well keeps state transitions easy to trace.
Model actions as events
An action is the smallest description of one interaction. Give it a type string and only the fields the reducer needs to compute the next state.
{ type: "task/added", id: 3, text: "Book a flight" }The type names the event, while id and text carry the payload. Include only the fields the reducer needs, nothing more. The slash in the name is optional; some teams use it to group actions by feature.
Write a pure reducer
A reducer receives state and an action, then returns the next state. Use a switch, wrap each case in braces, and return from every branch.
function tasksReducer(tasks, action) {
switch (action.type) {
case "task/added":
return [...tasks, { id: action.id, text: action.text }];
case "task/removed":
return tasks.filter((task) => task.id !== action.id);
default:
return tasks;
}
}Each branch builds a new array. Wrapping each case in braces stops variables from leaking between branches, and returning from every case prevents accidental fall-through. The default branch returns the same tasks reference, which tells React there is nothing new to render.
Connect the reducer with useReducer
Dispatch the action from an event handler, and React feeds it to the reducer on the next render.
import { useReducer } from "react";
function TaskList() {
const [tasks, dispatch] = useReducer(tasksReducer, []);
return (
<button onClick={() => dispatch({ type: "task/added", id: 4, text: "Pack" })}>
Add task
</button>
);
}Clicking the button dispatches one action. The reducer returns a new array with the extra task, and the component re-renders with the updated list.
Walk through one transition
Start with an empty list. Dispatching task/added with id 4 runs the reducer with the current empty array and that action, which returns a new array holding one task. React stores the result and re-renders, so the list shows a single item.
Dispatching task/removed with id 4 next filters the array back to empty.
Name actions after events, not setters
Action names should describe what happened, not what the state should become. A reset button that clears five fields should dispatch one reset action, not five setField actions.
- Good names: task/added, form/reset, filter/changed
- Poor names: setTasks, clearName, setEmailToEmpty
When you log every action, the good names reconstruct the user's session in order. See useReducer explained with examples for the dispatch side of this loop.
Keep the reducer pure
A reducer must not call APIs, read the clock, generate random values, or mutate its arguments. The same state and action must always return the same result.
function reducer(state, action) {
switch (action.type) {
case "task/added":
return { ...state, items: [...state.items, action.item] };
default:
return state;
}
}Returning a new object with a new items array is pure. Pushing into state.items directly would mutate state and hide the update. Read why reducers must be pure for the full reasoning.
Rune AI
Key Insights
- Actions describe what happened, not the desired state change.
- A reducer is pure and returns the next state.
- Return a new object or array instead of mutating.
- Name actions after events for clearer logs.
- Keep side effects in handlers, never in reducers.
Frequently Asked Questions
What shape should an action have?
Should a reducer use switch or if/else?
Can a reducer dispatch another action?
Conclusion
A good action names the event that happened, and a good reducer returns the next state without side effects or mutation. Keep actions minimal, name them after user intent, and let the reducer be the single place that decides how state changes.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.