Redux Reducers (Part 1): Handling Actions and Updating State #109
📖 Introduction
Following our exploration of Redux actions and action creators, this article dives into the heart of Redux: reducers. We will learn what reducers are, how they work, and how they are used to update the state in a Redux application.
📚 Prerequisites
Before we begin, please ensure you have a solid grasp of the following concepts:
- Redux actions and action creators.
- The concept of pure functions.
- The JavaScript
switch
statement.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ What Reducers Are: Understanding the role of reducers in a Redux application.
- ✅ The Structure of a Reducer: How to write a reducer function.
- ✅ Core Implementation: How to write a reducer for a to-do list application.
🧠 Section 1: The Core Concepts of Reducers
In Redux, a reducer is a pure function that takes the previous state and an action as arguments, and returns the next state. It's the only place where the state can be updated.
A reducer must be a pure function, which means that:
- It does not modify its arguments.
- It does not have any side effects.
- Given the same inputs, it will always return the same output.
The basic signature of a reducer is: (previousState, action) => newState
💻 Section 2: Deep Dive - Implementation and Walkthrough
Let's write a reducer for our to-do list application.
2.1 - The todos
Reducer
// reducers/todos.js
import { ADD_TODO, TOGGLE_TODO } from '../actionTypes';
const initialState = [];
function todos(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
id: action.payload.id,
text: action.payload.content,
completed: false
}
];
case TOGGLE_TODO:
return state.map(todo =>
(todo.id === action.payload.id)
? {...todo, completed: !todo.completed}
: todo
);
default:
return state;
}
}
export default todos;
Step-by-Step Code Breakdown:
function todos(state = initialState, action)
: We define our reducer function. It takes the currentstate
and anaction
as arguments, and we provide a default value for the state.switch (action.type)
: We use aswitch
statement to handle different action types.case ADD_TODO
: If the action type isADD_TODO
, we return a new array with the new to-do item added.case TOGGLE_TODO
: If the action type isTOGGLE_TODO
, we return a new array with thecompleted
property of the corresponding to-do item toggled.default
: If the action type doesn't match any of our cases, we return the current state.
2.2 - Combining Reducers
In a real application, you'll likely have multiple reducers, each managing a different part of the state. Redux provides a combineReducers
function to combine them into a single root reducer.
// reducers/index.js
import { combineReducers } from 'redux';
import todos from './todos';
import visibilityFilter from './visibilityFilter';
export default combineReducers({
todos,
visibilityFilter
});
💡 Conclusion & Key Takeaways
In this article, we've learned about Redux reducers and how they are used to update the state in a Redux application. We've seen how to write a simple reducer and how to combine multiple reducers into a single root reducer.
Let's summarize the key takeaways:
- Reducers are pure functions that take the previous state and an action, and return the next state.
- They are the only place where the state can be updated.
- The
combineReducers
function is used to combine multiple reducers into a single root reducer.
Challenge Yourself:
To solidify your understanding, try to create a visibilityFilter
reducer that handles the SET_VISIBILITY_FILTER
action.
➡️ Next Steps
You now have a solid understanding of Redux reducers. In the next article, "Redux Reducers (Part 2)", we will build a more practical example of how to use reducers in a React application.
Thank you for your dedication. Stay curious, and happy coding!
glossary
- Root Reducer: The main reducer in a Redux application, which is created by combining all of the other reducers.