tl;dr Redux Toolkit (RTK) saves time and reduces complexity, and the Redux stewards want it to be the industry-standard approach to architecturing Redux in apps
As a tech company, when you have the opportunity to automate repetitive, engineering-heavy tasks, it’s typically the smartest route to take in an ever-competitive, ever-complex landscape. These open engineering burdens waste humanpower, exhaust people’s time and energy, and tend to create a growing backlog of engineering debt that snowballs quickly.
At Taboola we’re no different in how we feel about inefficiencies due to unnecessary repetition. To prove this, we recently created a new team, Operations at Scale, in the Publisher domain of our Professional Services department. Our team is dedicated to automating repetitive operations and workflows and to addressing unsolved inefficiencies in the day-to-day work that serves our Publishers.
The Operations at Scale team tackles inefficiencies in a number of ways. One main focus is on the development of internal apps, which help remove barriers to entry for key user groups, like our Implementation teams or our Support engineers. Likewise, as a company with a large book of business, we naturally handle a lot of data, and the popular frontend stack for streamlining this data into user-friendly interfaces is React / Redux.
Despite an increasing number of options for state management in React like the useState hook, the Redux library remains a common choice for developers due to its reliable performance, powerful debugging capabilities, and scalability. There are some downsides however. These include:
- Redux often requires additional installation of other libraries, such as reselect (which can be used to create custom selectors)
- Implementing Redux requires a large amount of boilerplate and repetitive code (again not big fans of this at Taboola)
- A Redux store is complex to configure, with a steep learning curve
Enter Redux Toolkit (RTK). It describes itself as SOPE, which stands for “Simple, Opinionated, Powerful and Effective”. By abstracting over the familiar setup process that Redux requires and including common utilities like Immer (which greatly reduces the complexity of depending on spread operations re: state management), RTK solves a lot of the usual issues with Redux raised above. In Operations at Scale, we have found our code has become DRYer (Don’t Repeat Yourself), vastly more maintainable, and moving through faster development cycles since we’ve switched to RTK. Let’s look at some examples to illustrate the difference RTK can make to your codebase.
Using a simple counter example, an action creator function would look something like this in the standard Redux library:
RTK has a helper function that combines this into one clean declaration called createAction. The above could be written like this:
Already, the visible difference between the “legacy” implementation of Redux and the RTK implementation of Redux shows not only simplified code, but also the simplification of the domain knowledge necessary for understanding what Redux is doing under the hood.
Additionally, the reducer for this example counter in standard Redux is even more complex and requires a greater level of understanding for the engineers who work on the project. It would be like so:
RTK provides a utility called createReducer that simplifies creating reducer functions:
You may notice that the createReducer RTK code directly mutates the state, which is a violation of the Redux rule to keep reducer functions pure. One of the big advantages of RTK is its inclusion of the Immer package. This is used under-the-hood to drastically simplify immutable update logic by writing “mutative” code in reducers, thus removing the need for excessive JS spread syntax, often a necessary evil in standard Redux to ensure state is not mutated.
The RTK utilities we have looked at so far are useful, but you start to see the real power of switching from the standard Redux library when using the createSlice function. This combines createAction and createReducer into one, allowing you to define reducer functions, initial state, and generate the corresponding action creators and types, all from calling one function. Here’s how this would look with our counter example:
Hopefully, you can now begin to see how creating slices can dramatically simplify standard Redux syntax, with everything in one place, making developers lives much easier.
The reason we appreciate Immer and RTK in general is not only for the cleanliness it offers, but because it directly influences the architecture of our state and, likewise, the overall architecture of the app’s codebase. Regarding how we develop our ever-growing state object, we no longer have to spend (waste) time debating which layer of state a new layer should go into, how much of the current code base requires a refactor as a result of new state additions, etc. As noted, the inherent difficulty of a complex app’s Redux store can easily create a barrier of entry for any junior devs looking to onboard and contribute quickly to an app’s development, which is an issue we’ve encountered many times. So we see the reduction of complexity not only benefitting the velocity of our development process, but also our capacity since it allows us to onboard more junior devs due to the code’s intuitive characteristics like simple key-value assignments.
For large and complex apps, like the ones we work on at Taboola, this makes a substantial difference in development time and keeping our codebases clean. RTK has many other advantages and functions that have not all been covered here. Make sure to check out the official documentation to see what else RTK has to offer when moving from the standard Redux library.