Skip to main content

The Core Principles of Redux (Part 2): Changes Are Made with Pure Functions #106

📖 Introduction

Following our exploration of the first two core principles of Redux, this article dives into the third and final principle: changes are made with pure functions. We will learn about reducers 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:

  • The first two principles of Redux.
  • The concept of pure functions.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • The Third Principle of Redux: Understanding why changes must be made with pure functions.
  • Reducers: What they are and how they work.
  • Core Implementation: How to write a simple reducer for a to-do list application.

🧠 Section 1: The Core Concepts of Reducers

The third principle of Redux is that to specify how the state tree is transformed by actions, you write pure functions called reducers.

A reducer is a function that takes the previous state and an action as arguments, and returns the next state. It's called a reducer because it's the type of function you would pass to Array.prototype.reduce().

A reducer must be a pure function. This means that:

  1. It does not modify its arguments.
  2. It does not have any side effects (e.g., making API calls, calling non-pure functions).
  3. Given the same inputs, it will always return the same output.

By using pure functions, Redux ensures that the state is updated in a predictable and consistent way.


💻 Section 2: Deep Dive - Implementation and Walkthrough

Let's write a simple reducer for our to-do list application.

2.1 - The To-Do List Reducer

const initialState = {
todos: [],
visibilityFilter: 'SHOW_ALL'
};

function todoApp(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [
...state.todos,
{
id: action.id,
text: action.text,
completed: false
}
]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
(todo.id === action.id)
? {...todo, completed: !todo.completed}
: todo
)
};
default:
return state;
}
}

Step-by-Step Code Breakdown:

  1. function todoApp(state = initialState, action): We define our reducer function. It takes the current state and an action as arguments. We also provide a default value for the state.
  2. switch (action.type): We use a switch statement to handle different action types.
  3. case 'ADD_TODO': If the action type is ADD_TODO, we return a new state object with the new to-do item added to the todos array.
    • ...state: We use the spread syntax to copy the existing state.
    • ...state.todos: We use the spread syntax again to copy the existing todos array.
  4. case 'TOGGLE_TODO': If the action type is TOGGLE_TODO, we return a new state object with the completed property of the corresponding to-do item toggled.
  5. default: If the action type doesn't match any of our cases, we return the current state.

💡 Conclusion & Key Takeaways

In this article, we've learned about the third core principle of Redux: that changes are made with pure functions called reducers. This is the final piece of the puzzle that makes Redux a powerful and predictable state management solution.

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 way to change the state in a Redux application.
  • By using pure functions, Redux ensures that the state is updated in a predictable and consistent way.

Challenge Yourself: To solidify your understanding, try to add a REMOVE_TODO action to the todoApp reducer that removes a to-do item from the todos array.


➡️ Next Steps

You now have a solid understanding of the three core principles of Redux. In the next article, "Redux Actions and Action Creators (Part 1)", we will take a deeper dive into actions and learn how to use action creators to make our code more maintainable.

Thank you for your dedication. Stay curious, and happy coding!


glossary

  • Reducer: A pure function that takes the previous state and an action, and returns the next state.
  • Pure Function: A function that, given the same inputs, will always return the same output and has no side effects.
  • Side Effect: Any application state change that is observable outside the called function other than its return value.

Further Reading