♺⚛ Redux Toolkit
Third-party package developed by Redux team for better Redux store state management in React apps.
Challenges with complex state management
action
types can get exhausting. For different state properties, we will have different dispatchtype
and their correspondingactions
, along with theirpayloads
. This can get cumbersome to manage in a very large project.More data in state means more data to manage, and more data that we would need to return from within the check of
action.type
. We would need to copy a lot of redundant state properties just to update one state property.This can lead to a very large
reducer
function in the Reduxstore
which becomes difficult to manage in the end. This is the same problem that we faced in Reactcontext
API, where we declare a very large singular context with multiple states,i.e, complex setup.
All these above problems are solved by using another library called redux-toolkit
, which is developed by the same team/person who developed redux
. It is just an extra package that makes working with Redux a lot easier and convenient.
Redux ToolKit
Redux ToolKit
Installing the redux
toolkit
redux
toolkit
With this installed, we can remove the redux
dependency from our project, since toolkit
comes bundled with redux
.
Creating a slice
slice
We can also import the createReducer()
hook from the redux-toolkit
to create a reducer with certain enhancements, but the createSlice()
hook is more powerful than the createReducer
. With createSlice()
we create a slice of our global state. We could create different slices in different files, to make our code more maintainable.
The createSlice
takes in an object which has:
A
name
property.An
initialState
which is set to the default or initial state.an object (or a map ) called
reducers
which contains the list of all thereducer functions
that thisstate slice
needs.
Every method in the reducers
object is kind of an action type, which receives state
i.e, the current state for that slice, and no action
parameter, because the reducer function itself is called based upon the action/trigger by Redux.
So now, we do not need the if
checks, but instead we will identify these reducer functions and dispatch actions that target them.
Within these functions, we are now allowed to mutate the state directly, which we were previously not allowed to do! It seems like we are mutating the state directly, but behind the scenes, the Redux toolkit
uses another library called immer
, which takes care of this.
It detects code like this, where we directly mutate the state, and it automatically clones the existing state, create a new state, keep all the state that we are not editing, and override the state which we are editing in an immutable way.
It is easier for us to work with Redux because we do not have to make a copy of the state manually and keep all the code that we are not changing. So we just change the state that we want to update, and internally it is translated into immutable code.
Connect sliced state
sliced state
We have to register this counterSlice
, which is our state slice, with the Redux store
.
We can do it as follows [BAD PRACTICE]:
This works in theory, but will not be good for larger projects, where we have more than one slice of our state, and each slice will have their own set of reducer functions, so ideally this is not the best solution.
[BETTER SOLUTION]
This problem can be overcome by ditching the createStore
hook from the redux
library, and instead using a new hook from the reduxjs/toolkit
library, called configureStore
.
This hook also creates a store, but it makes merging multiple state reducers into one reducer easier thereafter. In the configureStore
hook, we now pass an object instead of a reducer function pointer. This object is a configuration object that is needed to configure the store with the appropriate config.
The configureStore
hook takes in a config object which contains:
reducer
, which can have a single reducer, or an object/map of reducers with any key with respective value pairs.Single reducer: where we combine all the
reducers
object from thecounterSlice
of the state and merge it into one big reducer.A map of reducers: where we have an object with key-value pairs, which in the end is combined into one big reducer with all the different reducers from different slices of the state.
Dispatching the actions
actions
The createSlice
hook automatically creates unique identifiers for our different reducers. To get a hold of these action identifiers, we can use the following dot
syntax:
These are called action creators
, and will create an action object
for us in the end.
This will create an action object with the unique identifier, like { type: 'some-unique-identifier-for-increment' }
. With this, we do not need to worry about creating action identifiers and creating the action objects on our own. Redux will do it for us behind the scenes.
We usually export all of our action creators
to be used in a component, from where we will dispatch these actions:
In case we have payloads in our reducer functions, we can either pass an object with key-value pairs, or we can send the data directly, but the key-value pair will have the default key as payload
, so we need that same key to access the data in the reducer function which has this payload.
Working with multiple slices
slices
Suppose we want to manage the counter state data and an authentication state data in the same Redux store. We could create two separate slices, since the two state are not related to each other.
Create initialState
initialState
So we create different initialStates
for counter
and auth
.
Create the slice
slice
Then, we create a new slice for the authentication state, with the same object properties, but with different values.
Add slice
to store
slice
to store
Next, we need to add this slice to our Redux store.
Previously, we had the following code, that only connected the counterSlice
to the Redux store:
So now, we will have to add an object to the reducer
property inside the configureStore
hook provided by reduxjs/toolkit
./code
Create actions
to be dispatched via the component
actions
to be dispatched via the componentReading the state value using useSelector()
useSelector()
Since we have more than one state object, we need to change the way we read the values of these states using the useSelector
hook from react-redux
. We need to use the key
name that we had provided to the reducers in the reducer
property in the configureStore
hook where we created the store and linked our reducer functions
.
Code Splitting
Since the store file contains the state slices, this file can become really large especially in a large React app, which has many state slices, and therefore becomes difficult to maintain and manage. It is common practice to move out our state slices into their own individual files, so that the store/index.js
file is easy to manage, and is readable.
Move the
initialState
for the slice into the state slice file.Move the
createSlice()
return value into the state slice file.Move the slice
actions
into the state slice file.Return only the state slice
reducer
from this state slice file and import only their respective reducers to be used in theindex,js
file for theconfigureStore
hook.
By making the above changes, we would need to change the imports as well, where ever necessary.
Finally, our index.js
file is easy to maintain and readable:
Last updated