Simplify Redux using Redux Toolkit

Time:2021-7-25

Learn about the Redux toolkit, a proven tool set for efficient Redux development. In this article, you will see why the Redux toolkit deserves more attention from the react community.

React and Redux are considered to be the best combination of management state in large-scale react applications. However, over time, Redux’s popularity has declined due to:

  • Configuring the Redux store is not easy.
  • We need several software packages to make Redux work with react.
  • Redux needs too much boilerplate code.

With these questions, the creator of ReduxDan AbramovPublished a publication entitled“You may not need Redux”The article suggests that people only use Redux when they need it, and follow other methods when developing less complex applications.

Problems solved by Redux Toolkit

The Redux Toolkit (formerly known as the Redux Starter Kit) provides some options to configure the global store and create actions and reducers more concisely by abstracting the Redux API as much as possible.

What does it include?

The Redux toolkit comes with some useful packages, such as immer, Redux thunk and reselect. It makes the work of react developers easier, allowing them to change the state directly (without dealing with immutability) and apply middleware such as thunk (dealing with asynchronous operations). It also uses a simple “selector” Library of Redux, reselect, to simplify the reducer function.

Simplify Redux using Redux Toolkit

What are the main functions of the Redux toolkit API?

The following is the API functions used by the Redux took kit, which is an abstraction of the existing Redux API functions. These functions do not change the process of Redux, but simplify them in a more readable and manageable way.

  • configureStore: create a Redux store instance like the original createstore from Redux, but accept a named option object and automatically set the Redux devtools extension.
  • createAction: accepts an action type string and returns a function created with the action of this type.
  • createReducer: accept the initial state value and action type lookup table to the reducer function, and create a reducer that handles all action types.
  • createSlice: accept an initial state and a lookup table with reducer name and function, and automatically generate action creator function, action type string and a reducer function.

You can use the above APIs to simplify the template code in Redux, especially usingcreateActionandcreateReducermethod. However, this can be further simplified by using createlicense, which can automatically generate action creator and reducer functions.

What’s special about createslice?

It is a helper function for generating memory slices. It accepts the slice name, initial state, and reducer function to return reducer, action type, and action creators.

First, let’s take a look at what reducers and actions look like in a traditional react reduce application.

Actions

import {GET_USERS,CREATE_USER,DELETE_USER} from "../constant/constants";
export const GetUsers = (data) => (dispatch) => {
 dispatch({
  type: GET_USERS,
  payload: data,
 });
};
export const CreateUser = (data) => (dispatch) => {
 dispatch({
  type: CREATE_USER,
  payload: data,
 });
};
export const DeleteUser = (data) => (dispatch) => {
 dispatch({
  type: DELETE_USER,
  payload: data,
 });
};

Reducers

import {GET_USERS,CREATE_USER,DELETE_USER} from "../constant/constants";
const initialState = {
 errorMessage: "",
 loading: false,
 users:[]
};
const UserReducer = (state = initialState, { payload }) => {
switch (type) {
 case GET_USERS:
  return { ...state, users: payload, loading: false };
case CREATE_USER:
  return { ...state, users: [payload,...state.users],
 loading: false };
case DELETE_USER:
  return { ...state, 
  users: state.users.filter((user) => user.id !== payload.id),
, loading: false };
default:
  return state;
 }
};
export default UserReducer;

Now, let’s see how to usecreateSliceSimplify and achieve the same functionality.

import { createSlice } from '@reduxjs/toolkit';
export const initialState = {
  users: [],
  loading: false,
  error: false,
};
const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    getUser: (state, action) => {
      state.users = action.payload;
      state.loading = true;
      state.error = false;
    },
    createUser: (state, action) => {
      state.users.unshift(action.payload);
      state.loading = false;
    },
    deleteUser: (state, action) => {
      state.users.filter((user) => user.id !== action.payload.id);
      state.loading = false;
    },
  },
});
export const { createUser, deleteUser, getUser } = userSlice.actions;
export default userSlice.reducer;

As you can see, now all actions and reducers are in a simple place. In traditional Redux applications, you need to manage each action and its corresponding action in the reducer. When using createlicense, you don’t need to use a switch to identify the action.

When it comes to abrupt States, a typical Redux process will throw errors. You will need special JavaScript strategies, such as spread operator and object assign, to overcome them. You don’t have to worry about using the redmer toolkit, so you don’t have to worry about changing the status. Because slice createdactionsandreducers, you can export them and use them in your components and store to configure Redux withoutactionsandreducersCreate separate files and directories as shown below.

import { configureStore } from "@reduxjs/toolkit";
import userSlice from "./features/user/userSlice";
export default configureStore({
 reducer: {
  user: userSlice,
 },
});

This storage can be useduseSelectoranduseDispatchThe Redux API is used directly from components. Note that you do not have to use any constants to identify operations or use any type.

Processing asynchronous Redux streams

To handle asynchronous actions, the Redux toolkit provides a special API method calledcreateAsyncThunk, it accepts a string identifier and a payload creator callback, executes the actual asynchronous logic, and returns a promise that will handle the scheduling of related actions according to the promise you return, as well as the action types that can be processed in your reducers.

import axios from "axios";
import { createAsyncThunk } from "@reduxjs/toolkit";
export const GetPosts = createAsyncThunk(
"post/getPosts", async () => await axios.get(`${BASE_URL}/posts`)
);
export const CreatePost = createAsyncThunk(
"post/createPost",async (post) => await axios.post(`${BASE_URL}/post`, post)
);

Different from the traditional data flow, thecreateAsyncThunkThe actions processed will be processed by the actions in the shardextraReducersPartial processing.

import { createSlice } from "@reduxjs/toolkit";
import { GetPosts, CreatePost } from "../../services";
export const initialState = {
  posts: [],
  loading: false,
  error: null,
};
export const postSlice = createSlice({
name: "post",
initialState: initialState,
extraReducers: {
   [GetPosts.fulfilled]: (state, action) => {
     state.posts = action.payload.data;
   },
   [GetPosts.rejected]: (state, action) => {
    state.posts = [];
   },
   [CreatePost.fulfilled]: (state, action) => {
   state.posts.unshift(action.payload.data);
   },
 },
});
export default postSlice.reducer;

Please note that inextraReducersInternally, you can handle the resolved(fulfilled)And rejected(rejected)Status.

Through these code snippets, you can see how the toolkit simplifies code in redux. I created oneRest example using Redux ToolkitFor your reference.

Last thought

According to my experience, when you start using Redux, Redux toolkit is a good choice. It simplifies code and helps manage Redux state by reducing template code.

Finally, like Redux, the Redux toolkit is not just built for react. We can use it with any other framework, such as angular.

You can refer toRedux toolkit documentationFind more information.

Thank you for reading!