Should We Switch from Redux to Redux ToolKit?

Jollen Moyani - Feb 28 - - Dev Community

In JavaScript development, Redux stands tall as a robust library designed to tackle the complexities of application state management. Historically, Redux has been a cornerstone in developers’ toolkits working on large-scale React applications. However, despite its initial prominence, several drawbacks have led to a decline in its popularity:

  • Complicated configuration setup.
  • Dependency on additional packages to use Redux efficiently.
  • Requires writing too much boilerplate code.

For more details, refer to the article You Might Not Need Redux.

Fortunately, the landscape of state management solutions has evolved, presenting the Redux Toolkit as an alternative. Developed in response to the aforementioned pain points, the Redux Toolkit offers a streamlined approach to development.

In this blog, we’ll see the advantages of Redux Toolkit, including its efficiency and simplicity compared to its predecessor.

What is the Redux Toolkit?

Redux Toolkit is a method to write Redux logic. It was introduced by simplifying common Redux use cases and addressing the major Redux drawbacks. It supports the most-used Redux add-ons, like Redux Thunk (for async tasks), Immer (for handling immutability in stores), and Reselect (to select a slice out of the global store).

Redux Toolkit works extensively in the background to simplify application state management by abstracting the Redux API.

As an example, let’s see a simple comparison between these two technologies to create a Redux store.

With Redux

// src/app/store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

export default configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});
Enter fullscreen mode Exit fullscreen mode

With Redux Toolkit:

// src/app/store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;

export default configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});
Enter fullscreen mode Exit fullscreen mode

As you can see in the previous example, Redux Toolkit reduces the boilerplate and complexity involved in setting up a Redux store compared to the classic Redux approach. Redux involves manually configuring middleware, reducers, and sometimes enhancers, whereas the Redux Toolkit uses configureStore to set up the store and manages reducers with createSlice. That makes Redux Toolkit the more appealing and efficient approach.

What is included in the Redux Toolkit?

Several Redux Toolkit API functions add much value as abstract versions of Redux API functions. They add more manageability and aid in streamlining the Redux flow.

We can use these to simplify the boilerplate code.

configureStore()

This function creates a Redux store instance and is an abstraction of the Redux createStore(). But the configuration here is simplified, enables the Redux DevTools Extension, and includes redux-thunk by default.

createAction()

This function defines an action creator function by accepting an action type string.

Refer to the following code example.

import { createAction } from '@reduxjs/toolkit';

// Create action creators.
const increment = createAction('counter/increment');
const decrement = createAction('counter/decrement');

// Passing a payload with the action.
const incrementByAmount = createAction('counter/incrementByAmount');
console.log(incrementByAmount(5));
Enter fullscreen mode Exit fullscreen mode

The createAction combines the action type constant and action creator function from Redux.

createReducer()

This helps us to create a reducer function more simply. It allows us to map action types directly to case reducer functions to update the state when an action is dispatched. In addition, it uses the Immer library automatically to simplify the immutable update logic with mutative code.

Refer to the following code example.

import { createReducer } from '@reduxjs/toolkit';

// Initial state.
const initialState = { value: 0 };

// Create reducer.
const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {
      state.value += 1;
    })
    .addCase(decrement, (state, action) => {
      state.value -= 1;
    })
    .addCase(incrementByAmount, (state, action) => {
      state.value += action.payload;
    });
});

export default counterReducer;
Enter fullscreen mode Exit fullscreen mode

createSlice()

This function automatically generates action creators and action types by accepting a slice name, the initial state, and an object of reducer functions. It generates a slice of the store and simplifies the process even more.

In Redux, we must manage the actions and their corresponding actions inside the reducers. But unlike in Redux, all the actions and reducers are present within the same slice when using createSlice.

createAsyncThunk()

This function accepts a Redux action type string and a callback function that should return a promise and abstract the recommended method of handling async lifecycles.

createEntityAdapter()

We can use this function to create a set of prebuilt reducers and selectors to perform CRUD operations on a normalized database.

The previous code snippets and the key features show how much easier it is to use the Redux Toolkit instead of the traditional Redux approach.

How to Use Redux Toolkit

In this tutorial, we’ll explore using Redux Toolkit to create a basic book management application. We’ll go through each step, from setting up the project to interacting with the Redux store in React components.

Step 1: Setting up the project

First, let’s create a new React project and install the necessary libraries:

// create a React app.
npx create-react-app app-name

// install Redux Toolkit and React Redux.
npm install @reduxjs/toolkit react-redux
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating the book slice

Next, we’ll create a Slice for managing books. This slice will handle adding and deleting books.

// src/features/book/bookSlice.js

import { createSlice } from '@reduxjs/toolkit';

export const bookSlice = createSlice({
 name: 'book',
 initialState: [],
 reducers: {
  addBook: (state, action) => {
   // Directly mutating the state is safe inside createSlice thanks to Immer.
   state.push(action.payload);
  },
  deleteBook: (state, action) => {
   return state.filter(book => book.id !== action.payload.id);
  },
 },
});

export const { addBook, deleteBook } = bookSlice.actions;
export default bookSlice.reducer;
Enter fullscreen mode Exit fullscreen mode

The addBook will take a book object as a payload and add it to the current state, whereas the deleteBook removes a book based on the provided ID.

Step 3: Configuring the store

Now, let’s configure the Redux store.

// src/app/store.js

import { configureStore } from '@reduxjs/toolkit';
import bookReducer from './../features/book/bookSlice';

export default configureStore({
 reducer: {
  book: bookReducer,
 },
});
Enter fullscreen mode Exit fullscreen mode

Step 4: Integrating the store with the application

Now, we can use the store we’ve created in our application by wrapping the app’s component tree with the Provider component from react-redux.

// src/index.js

// ...
import { Provider } from 'react-redux';
import store from './app/store';

// ...
    <Provider store={store}>
      <App />
    </Provider>
// ...
Enter fullscreen mode Exit fullscreen mode

Step 5: Interacting with components

Finally, we’ll interact with the Redux store from our components using hooks like useSelector to access the state and useDispatch to dispatch actions.

// src/features/book/BookComponent.js

// ...
import { addBook, deleteBook } from './bookSlice'; 

const BooksComponent = () => {
 const books = useSelector((state) => state.book);
 const dispatch = useDispatch();

 const [newBookTitle, setNewBookTitle] = useState('');

 const handleAddBook = (e) => {
  e.preventDefault();
  const newBook = {
   id: Date.now(),
   title: newBookTitle,
  };
  dispatch(addBook(newBook));
  setNewBookTitle(''); 
 };

 const handleDeleteBook = (bookId) => {
  dispatch(deleteBook({ id: bookId }));
 };

 return (
  // ... book list
  };

  export default BooksComponent;
Enter fullscreen mode Exit fullscreen mode

Thus, we’ve built a simple book management application using Redux Toolkit.

GitHub reference

For more details, refer to the complete sample project on GitHub.

Why use the Redux Toolkit?

As you’ve experienced in the previous example, the Redux Toolkit makes the development of Redux apps much faster and more straightforward.

We can use the Redux Toolkit at the beginning of a new project or as an incremental migration to an already-developed project.

Advantages:

  • An excellent set of features makes it more beginner-friendly than Redux.
  • When compared to Redux, the need for boilerplate code is significantly minimized. This helps developers focus more on the business logic than the configurations.
  • Redux Toolkit does not leave space for accidental mutations, minimizing the risk of potential Redux bugs.
  • Redux Toolkit comes bundled with Redux Thunk , so there is no need to manually go through middleware like Redux-Saga and Redux Thunk. It’s already built in.
  • It consolidates all action creators, reducers, and actions into single, manageable files using constructs like createSlice. This makes the code a lot more manageable and readable.
  • Redux Toolkit configures the Redux DevTools extension automatically, providing powerful tools for debugging and state monitoring.
  • We can effortlessly use the Redux Toolkit with Typescript.

So, why not use this optimized solution to gain maximum efficiency and manageability over Redux?

Final Thoughts

Redux Toolkit saves precious time you must invest in the project and even reduces the amount of code a developer must write. One of the best things about it is that its support is not limited to React but extends to frameworks like Angular, also.

Implementing the Redux Toolkit could be the step you need to take toward more simplified and efficient state management. I hope this article helped you get a clear picture of the transformative potential of Redux Toolkit.

The Syncfusion React JS UI component library is the only suite you will ever need to build an application. It contains over 80 high-performance, lightweight, modular, and responsive UI components in a single package.

Those familiar with Syncfusion can download the product setup from the Downloads page. If you’re new to Syncfusion, we welcome you to take advantage of our complimentary 30-day trial to explore our offerings firsthand.

If you have any questions or need assistance, please don’t hesitate to contact us through our support forum, support portal, or feedback portal. Our team is here and ready to assist you at every stage.

Thank you for reading!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .