Reduce boilerplate with Redux Toolkit

Carlos Ruana
Strands Tech Corner
6 min readMay 10, 2022

This article will try to convince you why using Redux Toolkit library is a must if you plan to use Redux or you are already using it.

This article does not discuss whether you should use Redux vs React context or any of the multiple other alternatives available.

TL;DR This is why:

  • You will have to write a lot less boilerplate code.
  • Writing actions and reducers way more easy.
  • Avoid adding many other libraries for immutability, entities and collection management, etc

The fist thing you need to know about Redux Toolkit is that is a just a wrapper on top of Redux that does not change any of the logic for creating and maintaining you application state. It adds some utilities to make you live much more easy.

configureStore

Although it is not the main star feature, configuring the store with Redux Toolkit it is also easier. Those are the main benefits:

  • Automatically adds redux-thunk middleware to the store
  • Redux DevTools Extension is already turned on

createSlice

This is where the magic starts, and now writing Redux logic is all about configuration. Give it a name, an initialState and your reducers logic and Redux Toolkit already provides you with:

  • createSlice uses the key of the reducers object (on the above example: increment, decrement, incrementbyAmount) to automatically generate action creators and action types with the same string name, which you are ready to export for dispatching from you application, so no more manually creating and maintaining constants on other files or writing complex action creators.
  • Those reducers are passed to createReducer() which provides immutable logic to update the state.

createReducer

It provides with some logic to avoid mutating the state by accident (uses Immer library internally) although it allows you to write the code as you were really mutating the state.

Lets see what that means with an easy example. First without using createReducer:

And now using it:

Although at first sight it seems anti-pattern assigning the new value directly to any part of the sate (at least it did to me), it is obvious how it avoids the mess of creating a copy of the previous state in a very nested object when you only want to do a single update.

Once more Redux Toolkit does the work for you and creates a copy of the new state object before merging it into the store.

In my team to get rid of Immutable.js was necessary as the performance benefits it provides were not worth the difficulties using the library API and some of the times we end up converting Immutable objects to regular objects to work with in the views, loosing all the performance benefits anyway. So Immer bundle size is lighter than Immutable one and together with the other Redux toolkit utilities, it provides everything we need. I would recommend using Immer with Redux even if you decide not to use Redux Toolkit.

createAction

Let’s see that one also better with an example on how normally action creator functions are build:

And how we can do it with just one line with Redux Toolkit library:

There are several things to notice here:

  • createAction returns the action creator you can export and then dispatch from within your application.
  • When you call that action creator with arguments this becomes automatically the action.payload object.
  • The string you pass to createAction (counter/increment in the example) becomes the action type you then can use in your reducer. See example below.

createAsyncThunk

Very similar to createAction but for the case of dispatching async actions. Also notice:

  • createAsyncThunk returns the thunk action creator you can export and then dispatch from within your application.
  • First parameter is the Redux action type, but in that case the string is not generating any reducer functions automatically as it does not know the details of your implementation. You should implement that instead on the extraReducers functions. In the example you can see the case of fetchUserbyId.fullfilled but createAsyncThunk will also generate fetchUserbyId.pending and fetchUserbyId.rejected action creators where you can add you loading logic or error handling.
  • Second parameter is the callback where you implement the async behavior (such as doing ajax calls) and should return a Promise.

createEntityAdapter

I really like that one, you can normalize (efficiently way of organizing data) your data entities, also with just one line.

On the example above you just need selectId if you collection of data does not have an “id” attribute or is named different, such as “bookId”. Also you can skip the sortComparer if the collection to normalize has already the order you need.

This is the most important functionality it provides:

  • It creates an easy to access structure for each collection { ids: [], entities: {} }. Very similar to what normalizr library provides. And although you can still use normalizer with createEntityAdapter our team decided the library was no longer needed for the needs of our applications.
  • Provides with plenty of CRUD functions to update your collection within your reducers. Take a look at the above example how easy is to add a new book to the collection by just using booksAdapter.addOne() or replace a full collection with booksAdpater.setAll(). We use it all the time on the fullfilled reducers when doing ajax calls, to update the state with its set of generated reducer functions for adding, updating, and removing entity instances from an entity state object.
  • More magic still!!! It contains a getSelectors() function which provides a set of very useful selectors (selectIds, selectEntities, selectAll, selectTotal, selectById) you can export. Those are the most used ones and Redux Toolkit includes them so you just have import them in your views and use the regular useSelector hook to get and work with your entities.

If you want to learn more the official site has plenty of nice examples and tutorials:

https://redux-toolkit.js.org/tutorials/overview#redux-essentials-a-real-world-example

Conclusion

This is a glimpse at the Redux Toolkit library. Each of the methods above provide with much more functionality and it includes many other utilities, but hopefully you got an idea of how it helps fast development on your Redux Store by reducing the common boilerplate and making all the required steps much easier.

Our team reduced so much the boilerplate that rather than having several files for our state management (actions, reducers, schemas, selectors) we can fit each of our reducers now into a single (not too long) slice file.

It is a light library and the learning curve is very low if you already know Redux. Even if people joining your team knows nothing about Redux they will learn faster to create a store with Redux Toolkit rather that without it.

--

--