Simplifying Redux with Redux Toolkit

Get to know the Redux Toolkit, an opinionated toolset for efficient Redux development.

Redux Toolkit is an opinionated, batteries-included toolset for efficient Redux development. In this article, you will see why the Redux Toolkit deserves more attention in the React community.

React and Redux believed to be the best combo for managing state in large-scale React applications. However, with time, the popularity of Redux fallen due to;

  • Configuring a Redux store is not simple.
  • We need several packages to get Redux to work with React.
  • Redux requires too much boilerplate code.

With these issues, the creator of Redux Dan Abramov published the article called You Might Not Need Redux, which advises people to use Redux only when it needs and to follow other methods when developing less complex applications.

The problem Redux Tool Kit solves

Redux Tool Kit(previously known as Redux Starter Kit) provides some options to configure the global store and create both actions and reducers more streamlined by abstracting the Redux API as much as possible.

What does it include?

Redux tool kit comes with several useful packages installed with it like Immer, Redux-Thunk, and Reselect. It makes life easier for React developers, allowing them to mutate state directly (Immer handle immutability) and applying middleware like Thunk (which handles async actions). It also uses Reselect, a simple “selector” library for Redux, to simplify reducer functions.

Redux toolkit dependencies

Share components between projects using Bit (Github).

Bit makes it simple to share and reuse independent components between projects.

Use it to maximize code reuse, keep a consistent design, speed-up delivery, and build apps that scale.

Bit supports Node, TypeScript, React, Vue, Angular, and more.

Exploring components shared on Bit.dev

Main features of Redux Tool Kit API?

The following API function is used by Redux Took Kit, which is an abstract of the existing Redux API function. These function does not change the flow of Redux but only streamline them in a more readable and manageable manner.

  • configureStore: Creates a Redux store instance like the original createStore from Redux, but accepts a named options object and sets up the Redux DevTools Extension automatically.
  • createAction: Accepts an action type string and returns an action creator function that uses that type.
  • createReducer: Accepts an initial state value and a lookup table of action types to reducer functions 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 APIs 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 is so special about createSlice?

It is a helper function that generates a store slice. It takes the slice’s name, the initial state, and the reducer function to return reducer, action types, and action creators.

First, let’s see how reducers and actions look like in traditional React-Redux applications.

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 we can simplify and achieve the same functionality by 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 the actions and reducers are in a simple place wherein a traditional redux application you need to manage every action and its corresponding action inside the reducer. when using createSlice you don’t need to use a switch to identify the action.

When it comes to mutating state, a typical Redux flow will throw errors and you will require special JavaScript tactics like spread operator and Object assign to overcome them. Since the Redux toolkit uses Immer, you do not have to worry about mutating the state. Since a slice creates the actions and reducers you can export them and use them in your component and in Store to configure the Redux without having separate files and directories for actions and reducers as below.

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

This store can be directly used from the component through redux APIs using useSelector and useDispatch. Notice that you don’t have to have any constants to identify the action or use any types.

Handling async Redux flows

To handle async actions Redux toolkit provides a special API method called createAsyncThunk which accepts a string identifier and a payload creator callback that performs the actual async logic and returns a promise that will handle the dispatching of the relevant actions based on the promise you return, and action types that you can handle 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, actions handled by createAsyncThunk will be handled by the section extraReducers inside a slice.

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;

Notice that inside extraReducers, you can handle both resolved (fulfilled) and rejected (rejected) states.

Through these code snippets, you can see how well does this toolkit simplifies the code in Redux. I have created a REST example that leverages Redux Toolkit for your reference.

Final thoughts

Based on my experience, Redux Toolkit is a great option to use when getting started with Redux. It simplifies the code and helps to manage the Redux state by reducing the boilerplate code.

Finally, just like Redux, Redux Toolkit is not built just for React. We can use it with any other frameworks such as Angular.

You can find more information on the Redux Toolkit by referring to their documentation.

Thank you for Reading !!!

Learn more


Simplifying Redux with Redux Toolkit was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Madushika Perera

Get to know the Redux Toolkit, an opinionated toolset for efficient Redux development.

Redux Toolkit is an opinionated, batteries-included toolset for efficient Redux development. In this article, you will see why the Redux Toolkit deserves more attention in the React community.

React and Redux believed to be the best combo for managing state in large-scale React applications. However, with time, the popularity of Redux fallen due to;

  • Configuring a Redux store is not simple.
  • We need several packages to get Redux to work with React.
  • Redux requires too much boilerplate code.

With these issues, the creator of Redux Dan Abramov published the article called You Might Not Need Redux, which advises people to use Redux only when it needs and to follow other methods when developing less complex applications.

The problem Redux Tool Kit solves

Redux Tool Kit(previously known as Redux Starter Kit) provides some options to configure the global store and create both actions and reducers more streamlined by abstracting the Redux API as much as possible.

What does it include?

Redux tool kit comes with several useful packages installed with it like Immer, Redux-Thunk, and Reselect. It makes life easier for React developers, allowing them to mutate state directly (Immer handle immutability) and applying middleware like Thunk (which handles async actions). It also uses Reselect, a simple “selector” library for Redux, to simplify reducer functions.

Redux toolkit dependencies

Share components between projects using Bit (Github).

Bit makes it simple to share and reuse independent components between projects.

Use it to maximize code reuse, keep a consistent design, speed-up delivery, and build apps that scale.

Bit supports Node, TypeScript, React, Vue, Angular, and more.

Exploring components shared on Bit.dev

Main features of Redux Tool Kit API?

The following API function is used by Redux Took Kit, which is an abstract of the existing Redux API function. These function does not change the flow of Redux but only streamline them in a more readable and manageable manner.

  • configureStore: Creates a Redux store instance like the original createStore from Redux, but accepts a named options object and sets up the Redux DevTools Extension automatically.
  • createAction: Accepts an action type string and returns an action creator function that uses that type.
  • createReducer: Accepts an initial state value and a lookup table of action types to reducer functions 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 APIs 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 is so special about createSlice?

It is a helper function that generates a store slice. It takes the slice’s name, the initial state, and the reducer function to return reducer, action types, and action creators.

First, let's see how reducers and actions look like in traditional React-Redux applications.

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 we can simplify and achieve the same functionality by 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 the actions and reducers are in a simple place wherein a traditional redux application you need to manage every action and its corresponding action inside the reducer. when using createSlice you don’t need to use a switch to identify the action.

When it comes to mutating state, a typical Redux flow will throw errors and you will require special JavaScript tactics like spread operator and Object assign to overcome them. Since the Redux toolkit uses Immer, you do not have to worry about mutating the state. Since a slice creates the actions and reducers you can export them and use them in your component and in Store to configure the Redux without having separate files and directories for actions and reducers as below.

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

This store can be directly used from the component through redux APIs using useSelector and useDispatch. Notice that you don’t have to have any constants to identify the action or use any types.

Handling async Redux flows

To handle async actions Redux toolkit provides a special API method called createAsyncThunk which accepts a string identifier and a payload creator callback that performs the actual async logic and returns a promise that will handle the dispatching of the relevant actions based on the promise you return, and action types that you can handle 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, actions handled by createAsyncThunk will be handled by the section extraReducers inside a slice.

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;

Notice that inside extraReducers, you can handle both resolved (fulfilled) and rejected (rejected) states.

Through these code snippets, you can see how well does this toolkit simplifies the code in Redux. I have created a REST example that leverages Redux Toolkit for your reference.

Final thoughts

Based on my experience, Redux Toolkit is a great option to use when getting started with Redux. It simplifies the code and helps to manage the Redux state by reducing the boilerplate code.

Finally, just like Redux, Redux Toolkit is not built just for React. We can use it with any other frameworks such as Angular.

You can find more information on the Redux Toolkit by referring to their documentation.

Thank you for Reading !!!

Learn more


Simplifying Redux with Redux Toolkit was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Madushika Perera


Print Share Comment Cite Upload Translate Updates
APA

Madushika Perera | Sciencx (2021-03-11T00:28:41+00:00) Simplifying Redux with Redux Toolkit. Retrieved from https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/

MLA
" » Simplifying Redux with Redux Toolkit." Madushika Perera | Sciencx - Thursday March 11, 2021, https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/
HARVARD
Madushika Perera | Sciencx Thursday March 11, 2021 » Simplifying Redux with Redux Toolkit., viewed ,<https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/>
VANCOUVER
Madushika Perera | Sciencx - » Simplifying Redux with Redux Toolkit. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/
CHICAGO
" » Simplifying Redux with Redux Toolkit." Madushika Perera | Sciencx - Accessed . https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/
IEEE
" » Simplifying Redux with Redux Toolkit." Madushika Perera | Sciencx [Online]. Available: https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/. [Accessed: ]
rf:citation
» Simplifying Redux with Redux Toolkit | Madushika Perera | Sciencx | https://www.scien.cx/2021/03/11/simplifying-redux-with-redux-toolkit/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.