Writing modern Redux in 2020 - Redux Toolkit
Vitor Capretz
Posted on January 19, 2020
I wanted to build a React Native app from scratch so I could learn new technologies and find out what I could bring to my workplace and make our app better. Trying out new technologies might be harder to do when you're working in a team for an app that already has millions of users and a couple of hundreds of contributors, such as we do at Klarna.
So I found a React Native tutorial which was simple enough for me to try stuff like react-native-navigation, styled-components and learn how to use Redux in a more modern way.
What do I mean by "Modern Redux"?
In tech, things evolve quickly and new versions of libraries we use on a daily basis are released with a frequency that makes us wonder every once in a while what the cost of updating such libraries is.
Keeping libraries up to date is important for a variety of reasons, e.g. security-vulnerabilities fixes, performance improvements and sometimes even easier implementation.
When talking about React, we recently had a huge update introducing Hooks, which enabled a lot of the libraries in the ecosystem to leverage this approach and to give developers the possibility of moving out from the class approach if they wanted to.
react-redux introduced some hooks too, which I felt was a great addition. The Redux team also introduced @reduxjs/toolkit which is what I'm going to talk about in this post.
Redux architecture and too much boilerplate
Quick disclaimer: I'm not recommending Redux nor am I going to discuss whether it's a good solution for your app, you make that decision according to your own needs and constraints.
When doing the React Native tutorial the author himself was constantly writing the default boilerplate code for setting up a Redux store with its action dispatchers, reducers and a bunch of files for each of these things.
A common approach is to have a folder that contains a file for the actions, a file for the reducer and probably a file for shared constants for action names.
But this causes discussions on whether or not they should be separate files, how you are going to deal with naming conventions, the selectors you're going to declare, etc.
Some other issues when setting up a Redux store also involve remembering not to mutate the store inside the reducers, choosing and setting up middlewares (e.g. for async actions).
Talking to a friend at Klarna about my frustrations and how I was bored just by thinking about all of this, he introduced me to the Redux Toolkit.
What is the Redux toolkit?
The above-mentioned issues (and probably a bunch more) are actually quite common and a lot of people have also shown their frustrations with them. So the Redux team came up with an opinionated toolkit to help us developers move faster by taking some decisions for us whilst making sure we don't break the conventions.
Keep in mind that anything that is opinionated might still bring frustrations for whoever doesn't agree with those opinions, but if they are from the very same people that maintain a library and set the standards for it, I would say it's good enough to trust it and move on so we can focus much more on the users and what they actually need instead of bikeshedding the same problems over and over again.
So the Redux toolkit is an opinionated tool that will handle all the common scenarios for you, removing a lot of the boilerplate code. While it will not solve all the problems you might have with Redux, it does bring benefits to the common scenarios.
Do check their docs for more examples, insights, and reasoning. But let's explore it a bit.
configureStore, createAction and createReducer
To create a simple Redux store you can use these three functions as replacements for the conventional approach.
I'll briefly introduce each of them and then show some code with each of them applied.
configureStore
replacescreateStore
, where you can still pass your reducers and middlewares as parameters.
It provides some useful default middlewares (some of them are applied only in a development environment),redux-thunk
is the chosen library as a solution for async actions.createAction
removes some of the boilerplate you usually have to write for each action, like thetype
parameter, how thepayload
looks and the argument name you will use.
The action'stype
is also something you need to share with the reducer so people usually have to set up a constants file to keep track of it.createReducer
is the last piece of the puzzle and the one with the most interesting differences compared to the conventional approach.
Instead of declaring aswitch/case
for each action type, you can use the actions themselves as parameters and have methods for how each of them should change the store's state.
BecausecreateReducer
uses immer (a library that creates the next immutable state tree while mutating the current one), you can actually mutate the state in any way you want, while still preserving the immutability required by Redux.
Code examples
So now you're probably wondering how all of this is actually implemented. I'll introduce some examples to show the basic scenarios.
The above implementation is one of the most straight forward ones, but it does show the main differences with the conventional approach:
- While you do have to pass a unique identifier to the actions, you don't need to share them with the reducers. Each action now has a
toString
method which returns that identifier, so we use that as keys to the reducer object; - We pass the initial state as the first parameter for
createReducer
and an object as the second one; - The reducer itself doesn't contain a
switch/case
statement, each action handler is now a key in an object, we don't need to worry about unknown actions here since it will simply return the current state as it is. - Because we are using
configureStore
, the toolkit is adding some default middlewares for us, you can check the exact ones in the API Reference forgetDefaultMiddleware
.
This next example shows how we can mutate the state inside each action handler and how the toolkit handles this for us:
Pushing items to an array or modifying a value inside it directly is not recommended when writing conventional Redux, while it's nice to have immutability it may cause more confusion and be less straightforward.
This is what it would look like if you were to avoid direct mutation:
You may or may not agree with this piece of code, but now we have the option to use it.
That's it! With these examples, I hope you now have some understanding of the Redux toolkit. The toolkit also introduces a function called createSlice
, I also didn't mention the hooks you can use with React and how to write async actions using redux-thunk
.
Let me know if you're interested in those subjects and I’ll write more posts in the future.
Conclusion
This was a short introduction with the goal to document what I discovered about the toolkit and also, hopefully, to spark some curiosity in you.
If you want to know more, please go to the Redux toolkit quick start and then try it out in your application if you're already using Redux.
Please let me know if you have any feedback about this article and follow me on Twitter - @vcapretz to keep in touch!
Cover image by Christian Holzinger on Unsplash
Posted on January 19, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.