What is Redux?
Redux is an open-source JavaScript library for managing application state using a simple constrained API designed as a predictable container for application state. It works similar to reducing function, a programming concept.
People have been using Redux for years . Redux was a revolutionary technology in React ecosystem. It allowed us to create a global “vault” with immutable data and fixed the prop drilling problem in our component tree. It is still a great tool for sharing immutable data in the application , which additionally scales well. Redux helps you write applications that behave consistently, run in different environments (client, server and native) and are easy to test.
Why should I not use Redux then?
With all these great advantages, you might be thinking why shouldn’t we use Redux?
It is often a triumph of form over content , and contains a large amount of boilerplate.
The main problem we face when using Redux and similar state management libraries is treating it as a cache for our backend state.
We fetch the data , add it to our store with reducer and action, and then re-fetch from time to time, hoping that we are up to date. We rely too much on Redux and we use it as a universal solution to all our problems.
When you’re developing a smaller application, it is not a bad idea to abandon Redux library altogether.
Often a large amount of code is required to make state management transparent, consistent and extensible. Additionally, the entry threshold in Redux is quite high, and understanding how action reductors and dispatches work can be quite overwhelming for beginners.
Great features come with a lot of complexity that can be an unnecessary burden on your project, especially if it is small.
When is it worth using Redux?
There are still certain circumstances, when application may actually have a huge amount of client-only synchronous states (such as visualization application or a music production application), in which case you will likely need a client state manager.
You should use React Redux especially if the UI is updated frequently, when many components have to respond to one action and the actions themselves are very complicated – then Redux is a good idea. Many companies still consider the Redux library as a mandatory technology that every developer should understand –it is visible, especially in the requirements of the labor market.
What to use instead of Redux?
React Query
The first very comprehensive and flexible state management library that comes to mind is React Query.
To make it easy to see the difference between React Query and Redux I will introduce the principles of both in the code. I implemented a simple TODO retrieved from the server using both methods, using pure JavaScript, React Hooks and Axios. It is also worth noting that Redux and React Query can be used simultaneously, one library does not exclude the other.
First, the Redux implementation:
Note that the above code doesn’t even support re-fetching, caching or invalidation . It just loads the data and stores it in the global store when the component is loaded. If you want to cache your data, there is still a lot of work to do. Needless to say, there will be more boilerplate to come.
Here’s the same example , already using React Query:
Much better.
By default, this example includes re-fetching, caching and invalidation with quite reasonable defaults. You can also set the caching configuration on a global level and forget about it completely – in most cases it will behave the way you want. For more information on how it works under the hood, see the React Query documentation. There are tons of configuration option available, which only illustrates the potential of this library.
Wherever you need this data, you can now use the useQuery hook with a unique key set ( „todos” in this case) and the asynchronous call used to retrieve the data. If the function is asynchronous , the implementation doesn’t matter — you can just easily use the Fetch API instead of Axios.
To change the state of our backend, React Query provides the useMutation hook.
For most applications, the truly globally available client state that remains after the asynchronous code has been migrated from Redux to React Query is usually very small.
Unstated
Here’s an example of state management using the Unstated library:
From a code transparency perspective, it’s a great library. It uses React hooks for all its state management logic.
If you are using TypeScript (which I strongly encourage you to do, if you haven’t tried it yet), it also has the advantage that the built-in TypeScript inference works better. As long as your non-standard hook is typed, everything else will just work.
But what are the real advantages of using Unstated that trump Redux?
- It is smaller It is approx.. 40 times smaller.
- It is faster. You can “component” the performance problem.
- It is easier to learn. You will need to know React & Context, Hooks from the start, use them they are really great.
- It is simpler in terms of integration. Integrate one component at a time and easily integrate with any React library.
- It is easier to test. Testing reducers is a waste of your time, make testing your components easier.
- It is easier to check types. Designed to bring out all the advantages of typing.
- It is minimalistic. It’s just React.
Redux Toolkit
Although it is not a separate technology, but only an add-on library to Redux, Redux Toolkit is a set of tools for efficient Redux creation. This is supposed to be a standard way to write Redux logic , and the developers of Redux strongly recommend using it.
It includes several utility functions that simplify the most common Redux use cases, including store configuration, defining reducers, immutable update logic, and even creating entire „slices” of the state at once, without manually typing any creator or action types. It also includes the most commonly used Redux add-ons such as Redux Thunk for async logic and Reselect for writing selector functions, so you can use them right away.
Redux Toolkit offers a configureStore feature that provides useful defaults such as standard extensions when creating a store. The function is waiting for the configuration object. The configuration object tries to map the parameters of the createStore function in a more understandable way.
The example of the configuration of a Redux store with various reducers, middleware, support for development tools ,the initial state and the enchancer can be seen in the example below:
In order to create an action in Redux, you usually need a constant for the type and an ActionCreator that uses that constant. This leads to more boilerplate. The Redux Toolkit createAction method combines these two steps into one. Here is an example of a normal ActionCreator and how you can create it with the Redux toolset.
Without Toolkit:
With Toolkit:
This feature helps to remove most of the standard code. One disadvantage is that you can no longer determine what format payload should be in. With the generated actions payload is not defined in more detail and you need to check the reductor that needs to be passed as payload.
If you want to go a step further, actions for the store could be generated automatically. For this purpose Redux Toolkit offers the createSlice method. The method requires slice name, initial state and a set of reducer functions as parameters.
The createSlice method then returns a set of generated elements – ActionCreators that match the keys in the Reducer module. The generated actions can receive the appropriate payload and can be easily used. However, generating these actions reaches its limit relatively quickly. It is no longer possible to map asynchronous actions, for example with Redux Thunk.
The following example shows how to use functions and the resulting action creators:
The createSlice method can significantly reduce the required boilerplate for some stores, if only because you do not need to return the entire state, you only need to overwrite a single property of a given state. However, it is not flexible and it should be checked whether it is suitable for a specific application.
Redux Toolkit includes a number of features that can simplify working with Redux. It covers many standard cases by default, but you still can configure it for more specific tasks. The Redux Toolkit package contains a collection of libraries that are already widely used and work well together. Thanks to these libraries and new functions of the Redux Toolkit many cases of boilerplate can be avoided and the syntax becomes a little clearer and easier to understand.
What about caching?
The library that is very good at caching is React Query. The components are kept in the cache, so when we receive the data from the server, they are displayed almost instantaneously, even if we refresh the DOM. This is crucial for any project where multiple components displayed simultaneously are downloaded from the server. Caching prevents overloading of each element, thus significantly improving performance and overall User Experience.
I have just created my new project – I’m installing Redux!
Many people take Redux for granted when they start working on their new project. Don’t forget that you can apply ideas from Redux without actually using it. For example consider the React component with local state:
Should you use this solution with the components that are nearly full of states? Probably not. This is, unless you have a plan to take advantage of this additional intermediation. These days having a plan is the key.
The Redux library is just a toolkit for “mounting” reducers to the global object of a single store. You can use as little or as much Redux as you want.
For example with the library like React Query you can get rid of :
- Connectors
- ActionCreators
- Middleware
- Reducers
- Loading/Error/Result states
- Contexts
After removing all of these things, you may be asking yourself a question: „Is it worth it to continue to use any client state manager for such a tiny global state?”
In my opinion, Redux library should be the last resort when choosing state management solutions , however the decision is entirely up to You!