Complex Reducers
Learn how to create, manage, and refactor complex reducers.
We'll cover the following...
Complex state management using Redux in a simple to-do app
To understand Redux in the context of a much larger state, let’s look at a more realistic example. The upcoming example describes a simple to-do app, and we’ll look at how to implement state management for this app. The to-do app will manage lists of to-do items and also contain a logged-in user area. The state will consist of two top-level properties:
todos
(oftype
array)user
(oftype
object)
This is reflected in our current state:
const initialState = Object.freeze({
user: {},
todos: [],
});
To ensure that a new state object is being created instead of mutating the previous object, the initial state object is wrapped by Object.freeze()
. If there is an attempt to mutate the **state object **directly, a TypeError
will be thrown:
'use strict';const initialState = Object.freeze({user: {},todos: [],});initialState.user = 'Lets change this state';
Let’s have a look at how a reducer function that manage the to-dos—meaning adding, removing, and changing the status of to-do items—and set the login area of a user:
const rootReducer = (state = initialState, action) => {switch (action.type) {case 'SET_USER': {return {user: {name: action.payload.name,accessToken: action.payload.accessToken,},todos: state.todos,};}case 'ADD_TODO': {return {user: state.user,todos: state.todos.concat({id: action.payload.id,text: action.payload.text,done: Boolean(action.payload.done),}),};}case 'REMOVE_TODO': {return {user: state.user,todos: state.todos.filter((todo) => todo.id !== action.payload),};}case 'CHANGE_TODO_STATUS': {return {user: state.user,todos: state.todos.map((todo) => {if (todo.id !== action.payload.id) {return todo;}return {...todo,done: action.payload.done,};}),};}default: {return state;}}};const store = createStore(rootReducer);
We won’t go into too much detail. However, a few things should be explained in depth. Let’s look at each switch
block, in which each case
block returns a new state object.
SET_USER
Let’s start with the case
block SET_USER
. The state object being created here changes the user
object and sets its name
property to action.payload.name
as well as the accessToken
property to action.payload.accessToken
. We could also set the user
to action.payload
, but this would mean that the complete payload of the action would be transferred to the user
object. Also, we have to ensure that the action.payload
is an object, so we don’t change the initial form of the user
object. This could become problematic if other parts of the reducer also access this object, and its type
had suddenly changed. We ignore all other properties in our example by explicitly accessing name
and accessToken
...