Redux remains one of the most talked-about libraries in the frontend ecosystem, and for good reason. Its predictability allows us to write applications that behave consistently across platforms (client, native, and server), it’s easily debuggable, and it works with any UI layer.
But the conversation around Redux isn’t always positive; indeed, the Redux team released Redux Toolkit last year in response to many of the lingering criticisms against it. In spite of this — or maybe because of it — a number of new alternatives for state management have cropped up, yielding the conclusion that Redux may finally be on its way out.
You already know the position this post takes based on the title — no, Redux isn’t dead. In defense of that stance, we will cover the following:
In software development, where there is a problem, we can be sure someone will soon create a solution. The problem Redux was created to solve is state management.
In React, we could have a global state in a top-level component. This top-level component passes state down as props to the components (i.e., the child components) that require the data.
class App extends Component { constructor() { super() this.state = { friends: [ { name:'Victor', age: 22 }, { name:'Matt', age: 30 }, { name:'Kate', age: 40 } ], } } render() { const allFriends = this.state.friends.map(friend => friend.name) return ( <div className="tc"> <h1 className="f1 pa">Friends</h1> <FriendList names ={allFriends}/> </div> ); } }
In the above code sample, the child component FriendList
receives the data allFriends
as props. It could also still be passed down once again. (I know I shouldn’t be doing this in a class component in 2021, but you get the idea.)
The state in the top-level component can always be updated. A child component would update the global state by receiving a function from the top component to update.
class App extends Component { constructor() { super() this.state = { friends: [ { name:'Victor', age: 22 }, { name:'Matt', age: 30 }, { name:'Kate', age: 40 } ], searchField: '' } } onSearchChange = (event) => { this.setState({searchField: event.target.value}) } render() { const filteredFriends = this.state.friends.filter(friend => { return friend.name.toLowerCase().includes(this.state.searchField) }) return ( <div className="tc"> <h1 className="f1 pa">Friends</h1> <SearchBox searchChange={this.onSearchChange}/> <FriendList names ={filteredFriends}/> </div> ); } }
In the above sample code, our searchField
state is always updated using the onSearchChange
function and passed down to the SearchBox
component.
Here’s what our SearchBox
looks like receiving the function for an update:
const SearchBox = ({searchChange}) => { return ( <div> <input className="br3 pa2 b bg-light-blue" type="search" placeholder="Search Robots" onChange={searchChange} /> </div> ); }
All this prop drilling and dealing with component C getting data from component A becomes a hassle when your application gets larger.
There have been recent arguments that we do not strictly need Redux, but the truth isn’t so clear-cut. You don’t realize you need Redux until you build an application for which state management is not so straightforward.
https://twitter.com/dan_abramov/status/699241546248536064
In the same vein, Pete Hunt, an ex-React team member, also said, “You’ll know when you need Flux. If you aren’t sure if you need it, you don’t need it.” If you are not sure if you need X, then you don’t need it.
So, to recap, we should use Redux if:
Just like every other tool, Redux has its downsides and trade-offs, too.
Before we dive into Redux alternatives, it must be clear that there are different architectures for state management. These patterns have all been used with React and other UI libraries to manage the state and its data flow.
The three patterns, neatly summed up in this tweet from Ilham Wahabi, are atomic, proxy, and flux.
This architecture is similar to what React uses for the Context API and useState
. This pattern allows you create your state as an atom and split them into smaller atoms, unlike the Redux store, which is a big container of all state.
Jotai is one example that uses this architecture. Let’s have a look a code sample.
import { atom } from 'jotai' const countAtom = atom(0) const friendAtom = atom('Matt') const friendsAtom = atom(['Victor', 'Matt', 'Kate'])
The state above is split into smaller pieces and treated as an atom. Using a particular atom in your component will look like this:
import { useAtom } from 'jotai' const Friend = () => { const [friend] = useAtom(friendAtom) return ( <div> <p>{friend}</p> </div ) }
Recoil is another Redux alternative that uses the atomic architecture.
This pattern uses the JavaScript Proxy
object to access the state. Proxy
wraps an object and mutates the traditional behaviors of the object. Its main purpose is to create custom behavior or redefine the fundamental operators.
MobX and Valtio are two popular state management libraries that use this architecture. According to Valtio, it turns the object you pass into it a self-aware proxy.
import { proxy, useSnapshot } from 'valtio' const bio = proxy({ age: 23, name: 'Victor' })
Make changes to the state from anywhere in your application:
bio.friends = { name: 'Matt' } bio.friends.amount = { number: 1 }
Or make a copy of the data from the proxy to be used for rendering:
function Bio() { const snap = useSnapshot(bio) return ( <div> {snap.name} <button onClick={() => ++bio.friends.amount}>+1</button> </div> ) }
Valtio forces you to read from the snapshot and mutate from its source. The component will definitely re-render the part of the state that was mutated.
Flux architecture is used by Redux and Zustand. This pattern has several components that are linked together to handle state: the actions, dispatcher, stores, and controller views.
We’ll use Zustand as an example; it is not as large as Redux and has far less boilerplate.
import create from 'zustand' const useStore = create(set => ({ myName: 'Victor', age: 23, friends: 0, increaseFriends: () => set(state => ({ friends: state.friends + 1 })), loseAllFriends: () => set({ friends: 0 }) }))
Zustand regards our store as a hook. The set
keyword combines the state into useStore
.
Using the state in our component is just easy.
function BearCounter() { const bears = useStore(state => state.bears) return <h1>{bears} around here ...</h1> } function Bio() { const name = useStore(state => state.myName) const increaseFriends = useStore(state => state.increaseFriends) return ( <h1>I am {name}</h1> <button onClick={increaseFriends}>Increase My Friends</button> ) }
Based on their architecture, the alternatives mentioned above — Jotai, Recoil, Zustand, and Valtio — have different methods of managing state, and in many ways, they arose as a response to the pattern Redux uses. Depending on your requirements, your application could benefit from using the atomic approach (Jotai or Recoil) or even the flux-like approach with Zustand rather than Redux thanks to its minimal API.
Redux has received a lot of flak from the JavaScript community, not only because of its “boilerplatey” code but because of its learning curve. Many devs miss the fact that Redux uses a design pattern that requires a lot of boilerplate code: the flux architecture, which makes use of its individual components for state management.
Flux uses the action component (method) to pass data to the dispatcher. The dispatcher gets the actions and helps to redistribute the property that holds the state to its callback. This property is what we know as the payload.
Then, the store acts as our state and logic container, which is linked to the callbacks. All these operations require a lot of boilerplate. Even those who bash Redux for the sheer amount of boilerplate have to admire this architecture.
Dan Abramov himself has made no secret of the fact that you probably don’t need Redux for your project. I would say you only need Redux when your application scales and becomes complex; a smaller app or a personal project could likely get by fine with the Context API.
Speaking of the Context API, it works well when you have to share global state data — no need to pass data as props all the time for each component. Indeed, sometimes this is enough, and a sophisticated external state management solution like Redux is overkill. We should be clear however, that Context is not a state management solution, but an easier way to take data to a nested component.
Likewise, some people have claimed that Redux is dead because React’s Hooks API is perfectly capable of handling state already, especially when used together with the Context API. That’s not totally false, but a lot of this backlash is a result of the stress of prop drilling. If you don’t want to have to drill props into components, then Redux should not be your choice.
My point is that much of the criticism against Redux is the result of misconceptions like these. Many devs use Redux poorly or use it when it isn’t necessary. It is best to understand your project before choosing Redux.
In many ways, the future is now thanks to Redux Toolkit (RTK). This toolkit helps with the abundance of boilerplate code Redux offers by default, providing a simplified store setup, reducers, and actions. It also includes all the packages commonly used alongside Redux so we don’t have to install them all individually —packages like Reselect, Redux Thunk, Immer, and more.
Installing this toolkit is easy:
# In your already existing React application, run either of these commands # npm npm install @reduxjs/toolkit # Yarn yarn add @reduxjs/toolkit
The APIs that RTK provides are createStore()
, createReducer()
, createAction()
, and createSlice()
, which help to simplify the Redux boilerplate code. This one improvement to the Redux development.
createSlice()
makes your reducers and actions for you and connects them as well. React Redux also has the useDispatch
and useSelector
APIs now, which means you can connect to the dispatch actions and store without having to do use a HOC.
In my own opinion, Redux is never going anywhere in the coming years because it is such a great fit with React. There is no reliance or dependency on either of these libraries; React will be used on the view side of your application, while Redux handles the state.
Hooks and the Context still don’t provide as much power as Redux in terms of state management. In fact, it has been clearly explained that they are not a true state management solution. They’re just state providers — you still have to provide the logic in the components.
Redux employs a pub/sub architecture: your component is subscribed to state changes, and the component can also publish state changes using the dispatch. You might try employing the Context API in this scenario, but you’d likely end up having to use Redux in the end. There are firsthand stories out there about how using Context for state management can kill application performance.
The point is that Redux gives you more than state management. From the design pattern to the easy maintenance and scalability as the application grows, you’re able to decouple your state management logic from your UI layer. Redux is still useful, still has a future, and is most definitely not dead.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
2 Replies to "Redux isn’t dead"
Hi, I’m the primary Redux maintainer. I’d like to point out that I’ve covered this topic _repeatedly_ over the last few years 🙂 This post is definitely on the right track (and especially the mentions of Redux Toolkit!), but it’s worth pointing to the many resources I’ve written on these topics already for additional info. Some of the key posts I’ve written around this are:
Redux – Not Dead Yet!: obviously the same thesis as this post, but with an emphasis on how Redux relates to other tools
When (and when not) to Reach for Redux: A podcast transcript excerpt where I discuss some of the purposes and use cases for Redux and how tools like React Query and Apollo overlap with those scenarios
A vidcast talk where I cover current Redux usage stats
Why React Context is Not a “State Management” Tool (and Why It Doesn’t Replace Redux): an extensive post where I cover the differences in capabilities and use cases for Context, useReducer, and Redux, and when it makes sense to use either of them
Redux Toolkit 1.0: covers the intent and purpose behind the creation of Redux Toolkit and what problems it’s trying to solve
Hello boss!
Can you help review this library and see what you think of it: https://myckhel.github.io/use-redux-state-hook/
Thanks in addy.