Understanding Redux and Redux Toolkit

Vishant Singh
6 min readJan 23, 2023

Is this the right state management solution for you?

Redux toolkit is a Javascript library which builds on top of Redux to provide a state management solution for applications. It is often used in conjunction with react and react-native, but it can be used with a wide of frameworks or libraries.

What is Redux?

As applications become larger and data needs to be shared across screens and components a shared global state is required. This is where redux comes in, it stores the application state in a single immutable object called the store.

At its core, redux can be broken down into three main sections; Views, Actions, and State.

Source

๐Ÿ–ฅ Views

Views are the UI elements that exist in an application like a button, list or slider. When a user interacts with these views, i.e presses a button, an action is dispatched to the store.

๐ŸŽฌ Actions

Actions are pure objects that describe the change that should be made to the store. They always contain a type field (a string value which is a descriptive name for the action) and often a payload which contains additional parameters.

Actions are typically dispatched by a react component but can also be dispatched by other actions in more complex cases. An example of an action to add a number to a counter can be seen below.

const addToCountAction = {
type: 'addToCount',
payload: {
numberToAdd: 100,
}
}

Action creators are functions that build and return an action. In our case, the number we want to add to the count could be dynamic so to achieve this we could have an action creator as seen below.

const addToCountAction = (numberToAdd) =>  {
type: 'addToCount',
payload: {
numberToAdd
}
}

๐Ÿ—„ State

The store is an immutable object tree, meaning it can not be modified directly. State updates are done through reducers. A reducer acts as an event listener to a particular action. They are pure functions that take in the current state of the store, an action, and return a new state.

To preserve the immutability principle of redux the reducer must not modify the original state but make immutable updates, by copying the existing state and making changes to the copied values

const initialState = { count: 0 }

function counterReducer(state = initialState, action) {
switch (action.type) {
case 'addToCount': {
const { numberToAdd } = action.payload;
return {
...state,
count: state.value + numberToAdd
};
}
default: {
return state;
}
}
};

Screens, buttons and other UI components can subscribe to the state and update the UI to reflect changes to the state. These three elements can be seen working together in effect in the gif below from the official redux documentation.

Source

Advantages of Redux

๐ŸŒŽ Centralised

Redux makes it easy to share data between different parts of the application. This is because all the state is stored in a single place, and any part of the application can access the state by connecting to the store. This means we can use state from any component or screen we want.

๐Ÿ”Ž Predictable and Easy to Debug

The state is stored in a single place and all the changes to the state are made only through actions and reducers. Since the state itself is immutable and if the same state and action are passed to a reducer, the same result is always produced.

This means we can easily debug the application and time travel through a log of actions and state updates. There are many developer-friendly tools that provide this functionality including flipper and react-devtools.

โšก๏ธ Scaleable, Performant and Flexible

There is no limit to the number of actions and reducers you can have and state updates are generally fast. Additionally, components in react can be updated to only re-render when there is a visual change based on the state rather than every state update optimizing performance further.

Redux also offers the ability to add middlewares and enhancers in the configuration. Commonly used middlewares include redux-thunk which allows actions to be functions rather than just pure objects.

Where does Redux Toolkit fit in?

While redux is powerful and there are a plethora of advantages associated with it, there are a few disadvantages with it that Redux-Toolkit aims to solve. Redux-Toolkit is an optional library that provides functionality to address the most common complaints and disadvantages of redux.

๐Ÿ“œ Verboseness

One of the main disadvantages of basic redux is its boilerplate nature. To have a state for a value you need to have an action creator to create actions, dispatch the actions, reducers to perform state updates, and bind the state to the components.

This has to happen for each additional section of the state and this can become cumbersome and verbose. Redux toolkit aims to solve this by provides out of the box functions such as createAction(), createReducer(), and createSlice() which uses both createActions()and createReducer() underneath and automatically generates action creators and action types.

The example below shows how the createSlice() function can be used with the counter example below.

import { createSlice } from '@reduxjs/toolkit'

export const counterSlice = createSlice({
name: 'counter',
initialState: {
counter: 0
},
reducers: {
addToCount: (state, action) => {
state.count += action.payload.numberToAdd
}
}
})

export const { addToCount } = counterSlice.actions

export default counterSlice.reducer

๐Ÿ›‘ Prevents Common Errors

Basic redux is susceptible to a number of different issues that redux toolkit aims to solve by including commonly used libraries under the hood. This includes the following issues:

Resolving Accidental Mutations
In reducers, it can be easy to assign a value to state without performing a copy, especially if it is a nested value.

Redux toolkit solves this by using the Immer library in the createSlice() and createReducer() functions which means that direct โ€œmutationsโ€ to state are safe. Note this can be seen in the example in the addToCount reducer above.

Redux toolkit also has an immutability check middleware in the default middleware as part of the configuration. This middleware throws an error if a mutation to the state is detected.

Checks State is Serializable
It is possible to store non-plain-JS-data values such as functions, promises and symbols in the state. This should not happen, and Redux toolkit provides an additional serializability check middleware in the default middleware that logs an error when this occurs.

๐Ÿ›  Simplifies Store Configuration

Store configuration for redux can be complex and verbose, especially when a lot of different middleware, enhancers and reducers are included. Redux toolkit provides a function to configure the store called configureStore(). An example of this can be found below.

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
reducer: {
counter: counterReducer
}
})

While this may look simple, multiple improvements are made over the default createStore() function which it wraps. This includes automatically combining reducers (instead of having to use the combineReducers() in base redux), enabling react-devtools by default and including default middleware such as redux-thunk (a middleware for side-effects)

A Final Word

Selecting a state management solution can be a difficult proposition as it has a direct effect on the rest of the application and can be quite difficult to change once ingrained.

Redux is an excellent choice for state management for react and react-native applications. It has an easy-to-use pattern and provides a high-performant and scalable solution, no matter the size of your application.

With Redux toolkit it becomes easy to configure and many of the issues and complaints about Redux become moot. If youโ€™re looking for a state management solution for your application, look no further than Redux with Redux toolkit.

--

--