How to simplify Redux with Redux Toolkit

How to simplify Redux with Redux Toolkit

Get to know Redux Toolkit, a proven toolset for efficient Redux development. In this article, you’ll see why Redux Toolkit deserves more attention from the React community.

React and Redux are considered the best combination for managing state in large-scale React applications. However, over time, Redux's popularity declined due to:

  • Configuring Redux Store is not simple.
  • We need a few packages to make Redux work with React.
  • Redux requires too much boilerplate code.

With these questions, Dan Abramov, the creator of Redux, published an article titled "You May Not Need Redux", suggesting that people use Redux only when needed, and follow other methods when developing less complex applications.

Problems Redux Toolkit solves

Redux Toolkit (formerly Redux Starter Kit) provides options for configuring a global store and making creating actions and reducers more streamlined by abstracting the Redux API as much as possible.

What does it include?

Redux Toolkit comes with some useful packages like Immer, Redux-Thunk, and Reselect. It makes life much easier for React developers, allowing them to mutate state directly (without dealing with immutability), and apply middleware like Thunk (which handles asynchronous operations). It also uses Reselect, a simple "selector" library for Redux, to simplify the reducer function.

What are the main features of the Redux Toolkit API?

Following are the API functions used by Redux Took Kit, which is an abstraction of the existing Redux API functions. These functions do not change the Redux flows, they just simplify them in a more readable and manageable way.

  • configureStore: Creates a Redux store instance just like the original createStore from Redux, but accepts a named options object and automatically sets up the Redux DevTools extension.
  • createAction: Accepts an Action type string and returns an Action creation function that uses that type.
  • createReducer: Accepts the initial state value and a lookup table of action types to the reducer function and creates a reducer that handles all action types.
  • createSlice: accepts an initial state and a lookup table with reducer names and functions, and automatically generates action creator functions, action type strings, and a reducer function.

You can use the above API to simplify the boilerplate code in Redux, especially using the createAction and createReducer methods. However, this can be further simplified using createSlice, which automatically generates action creator and reducer functions.

What's special about createSlice?

It is a helper function that generates a memory slice. It accepts the name of the slice, the initial state, and a reducer function to return the reducer, action types, and action creators.

First, let's look at what reducers and actions look like in a traditional React-Redux 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 simplify and achieve the same functionality using createSlice.

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 one simple place, while in traditional redux application you need to manage each action and its corresponding action in reducer, when using createSlice you don't need to use switch to identify action.

A typical Redux flow will throw errors when it comes to mutating state, and you will need special JavaScript strategies like spread operator and Object assign to overcome them. Since Redux Toolkit uses Immer, you don't have to worry about mutating the state. Since slice creates actions and reducers, you can export them and use them in your components and Store to configure Redux without having to create separate files and directories for actions and reducers, as shown below.

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

This store can be used directly from components via the redux api using useSelector and useDispatch. Note that you don't have to use any constants to identify the operations or use any types.

Handling asynchronous Redux flows

To handle asynchronous actions, Redux Toolkit provides a special API method called createAsyncThunk that accepts a string identifier and a payload creator callback, performs the actual asynchronous logic, and returns a Promise that will handle the dispatch of the associated action based on the Promise you return, and the action types that can be handled 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)
);

Unlike traditional data flows, the actions handled by createAsyncThunk will be handled by the extraReducers section within the shard.

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;

Note that inside the extraReducers you can handle both fulfilled and rejected states.

With these code snippets, you can see how well this toolkit can simplify your code in Redux. I created a REST example utilizing Redux Toolkit for your reference.

Final Thoughts

In my experience, Redux Toolkit is a great choice when getting started with Redux. It simplifies the code and helps manage the Redux state by reducing boilerplate code.

Finally, just like Redux, Redux Toolkit isn’t built just for React. We can use it with any other framework like Angular.

You can find more information by referring to the Redux Toolkit's documentation.

Thank you for reading!

The above is the details of how to use Redux Toolkit to simplify Redux. For more information about using Redux Toolkit to simplify Redux, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Implementation steps for setting up the React+Ant Design development environment
  • How to build a React project with Vite
  • React example of how to get the value of the input box
  • React implements the sample code of Radio component
  • Let's talk about my understanding and application of React Context
  • React hooks introductory tutorial
  • Detailed process of creating a VR panoramic project using React and Threejs
  • A brief talk about React Router's history
  • React uses routing to redirect to the login interface

<<:  Hbase Getting Started

>>:  MYSQL replaces the time (year, month, day) field with unchanged hours, minutes, and seconds. Example analysis

Recommend

js implements a simple countdown

This article example shares the specific code of ...

Use CSS to easily implement some frequently appearing weird buttons

background In the group, some students will ask r...

Use of hasOwnProperty method of js attribute object

Object's hasOwnProperty() method returns a Bo...

Complete steps for mounting a new data disk in CentOS7

Preface I just bought a new VPS. The data disk of...

This article takes you to explore NULL in MySQL

Table of contents Preface NULL in MySQL 2 NULL oc...

Implementation example of uploading multiple attachments in Vue

Table of contents Preface Core code File shows pa...

CSS Naming: BEM, scoped CSS, CSS modules and CSS-in-JS explained

The scope of css is global. As the project gets b...

How to upload and download files between Linux server and Windows system

Background: Linux server file upload and download...

Vue+Echart bar chart realizes epidemic data statistics

Table of contents 1. First install echarts in the...

MySQL GROUP_CONCAT limitation solution

effect: The GROUP_CONCAT function can concatenate...

Alibaba Cloud Server Ubuntu Configuration Tutorial

Since Alibaba Cloud's import of custom Ubuntu...

MySQL 5.7.16 free installation version graphic tutorial under Linux

This article shares the MySQL 5.7.16 free install...