Back • 24/10/2024
Redux is Dead. Use Zustand Instead...
Written by Melvyn Malherbe on 24/10/2024
Redux is dead. Sure, it's still widely used, but for new projects, I advise against it, especially with the new way of doing things.
The solution now consists of two powerful libraries:
Tanstack Query
for managing server-stateZustand
for managing client-state
Especially since most states are now stored in server components and no longer need to be on the front-end.
Why is Redux Dead?
Redux was the first really popular state management library because, at the time, managing state in React wasn't easy.
Redux was used for applications at the level of Meta ADS
with thousands of menus, sub-menus, and API calls from all sides.
The state in Meta
is very complex to manage and maintain.
Redux
allows adding states, managed in "flux," enabling each part of the application to define its "reducer" to communicate with other parts of the application.
Redux
was a revolution for:
- managing the state of complex applications
- working in teams without stepping on each other's toes
But now it's used by teams of 3 developers on e-commerce sites, you start to see the problem.
Server Components
The new philosophy of React, which pushes towards Server Components
, greatly reduces the need for such a complex state manager.
Most data is no longer stored on the client side. When you see this code:
export default async function ProductPage() {
const products = await prisma.product.findMany();
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
Here, in this Server Component
, there is no client. Redux couldn't even exist. There's simply no state; we're just displaying a list of products.
But back in the day, you would have needed a useEffect
to fetch the data and store it in the state.
In summary, Server Components
change many paradigms and further reduce the need for such a complex state manager.
Server-state
However, sometimes you need to store the Server State
, meaning the state of our server.
For that, Tanstack Query
has established itself as an indispensable leader.
It manages a "cache" of all your data and allows you to update your Server State
whenever you want.
'use client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
export const ProductList = () => {
const queryClient = useQueryClient();
const {
data: products,
error,
isLoading,
} = useQuery({
queryKey: ['products'],
queryFn: async () => {
// get call
},
});
const mutation = useMutation({
mutationFn: async (newProduct) => {
// fetch call
},
onSuccess: () => {
queryClient.invalidateQueries(['products']);
},
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
<button onClick={handleAddProduct}>Add Product</button>
</div>
);
};
Here, you can easily fetch and mutate data; it's simple, efficient, and made for this purpose.
Tanstack Query
allows us to manage all the Server State that depends on asynchronous data to function.
But sometimes, there are client-side things. For example, the current state of a Sidebar.
When we talk about Server-state
or Client-state
, understand that everything
here is stored on the client side. We're talking more about the data it contains.
The Server-state
contains data from the Server
, while the Client-state
contains data from the Client
.
Client-state
For client State, the leader is Zustand
, which allows you to create stores that would replace a useContext
placed in App.tsx
.
You can easily create a store:
import { create } from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
And then, you can easily use your store:
import { useStore } from './store';
export const Counter = () => {
const { count, increment } = useStore();
return (
<div>
<p>Current count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
This store is accessible everywhere in your application, even outside of React:
import { useStore } from './store';
// Possible to use it outside of React
const increment = () => {
useStore.getState().increment();
};
Zustand
, unlike Redux
, has a "micro" approach to stores. There are no reducers, no dispatch
, and no action
.
Instead, you'll create many stores; in an application, you might have:
useSidebarStore
to manage the sidebar stateuseEditorStore
to manage the editor state- etc...
One store = one feature.
Why not use useContext
? First, because you'll have more code. But also because Zustand
supports selectors that allow you to optimize renders.
When a context changes, all components consuming it are re-rendered, even those not using the modified data.
Whereas with zustand
, it's possible to select the data:
import { useStore } from './store';
export const CounterIncrement = () => {
const count = useStore((state) => state.increment);
return (
<div>
<button onClick={count}>Increment</button>
</div>
);
};
This component will never be re-rendered because the increment
method will never change (normally).
When to Use Redux
?
Redux
is dead in the vast majority of applications. If your application heavily depends on server-side data:
- use
Server Components
- use
Tanstack Query
If your application needs a bit of client-side data:
- use
Zustand
But in the rare case where you're creating an application as complex as Meta ADS, or a ticket manager where a lot of data is interrelated and it's a joyful mess... good luck.
But also here, in this rare case representing 5% of applications, use Redux
.
Conclusion
Redux
is almost dead... at least for the majority of applications. I've shared my advice; it's up to you to follow it.
You can learn to better use React and Zustand in my BegiNReact course:
Le meilleur moyen d'apprendre React !
Rejoins par développeurs, cette formation reçoit une note de 4.7 / 5 🚀
Reçois 12 leçons premium pour maîtriser React et faire partie des meilleurs
You might also want to learn why you don't need useCallback or the difference between a prop and a state in React.