State Management Moderno em 2025: Por Que Zustand e Jotai Estão Superando Redux
Olá HaWkers, durante anos Redux foi o padrão ouro para gerenciamento de estado em React. Mas em 2025, desenvolvedores estão abandonando Redux em massa por soluções mais simples e modernas.
Zustand e Jotai representam uma nova geração de state management: menos boilerplate, melhor performance, e developer experience superior.
O Problema com Redux
// Redux - Muito boilerplate para algo simples
// actions/counter.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
// reducers/counter.js
const initialState = { count: 0 };
export default function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
}
// store.js
import { createStore, combineReducers } from 'redux';
import counterReducer from './reducers/counter';
const rootReducer = combineReducers({
counter: counterReducer
});
export const store = createStore(rootReducer);
// Component.jsx
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions/counter';
function Counter() {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
// 6 arquivos, ~60 linhas para um contador! 😱
Zustand: Simplicidade Extrema
// store/counter.js - TODO o state management em um arquivo!
import { create } from 'zustand';
export const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
// Component.jsx
import { useCounterStore } from './store/counter';
function Counter() {
const { count, increment, decrement } = useCounterStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
// 2 arquivos, ~20 linhas. Muito mais simples! ✨Zustand Features Avançadas
// store/user.js - Store completa com middleware
import { create } from 'zustand';
import { persist, devtools } from 'zustand/middleware';
export const useUserStore = create(
devtools(
persist(
(set, get) => ({
user: null,
token: null,
isAuthenticated: false,
// Actions
login: async (email, password) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password })
});
const { user, token } = await response.json();
set({
user,
token,
isAuthenticated: true
});
},
logout: () => set({
user: null,
token: null,
isAuthenticated: false
}),
updateProfile: (updates) => set((state) => ({
user: { ...state.user, ...updates }
})),
// Selectors computados
get fullName() {
const { user } = get();
return user ? `${user.firstName} ${user.lastName}` : '';
}
}),
{
name: 'user-storage', // LocalStorage key
partialize: (state) => ({
token: state.token,
user: state.user
})
}
)
)
);
Jotai: Atomic State Management
// atoms/counter.js
import { atom } from 'jotai';
// Atoms primitivos
export const countAtom = atom(0);
// Derived atoms (computed)
export const doubledCountAtom = atom((get) => get(countAtom) * 2);
// Write-only atom (actions)
export const incrementAtom = atom(
null,
(get, set) => set(countAtom, get(countAtom) + 1)
);
export const decrementAtom = atom(
null,
(get, set) => set(countAtom, get(countAtom) - 1)
);
// Component.jsx
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import { countAtom, doubledCountAtom, incrementAtom } from './atoms/counter';
function Counter() {
const count = useAtomValue(countAtom);
const doubled = useAtomValue(doubledCountAtom);
const increment = useSetAtom(incrementAtom);
return (
<div>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<button onClick={increment}>+</button>
</div>
);
}Jotai com Async Data
// atoms/users.js
import { atom } from 'jotai';
// Async atom - busca dados automaticamente
export const usersAtom = atom(async () => {
const response = await fetch('/api/users');
return response.json();
});
// Atom com refresh
export const refreshUsersAtom = atom(null, async (get, set) => {
const users = await fetch('/api/users').then(r => r.json());
set(usersAtom, users);
});
// Filtered atom
export const activeUsersAtom = atom((get) => {
const users = get(usersAtom);
return users.filter(user => user.active);
});
// Component.jsx - Suspense out-of-the-box!
import { Suspense } from 'react';
import { useAtomValue } from 'jotai';
import { usersAtom, activeUsersAtom } from './atoms/users';
function UserList() {
const activeUsers = useAtomValue(activeUsersAtom);
return (
<ul>
{activeUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// App.jsx
function App() {
return (
<Suspense fallback={<div>Loading users...</div>}>
<UserList />
</Suspense>
);
}
Comparação: Redux vs Zustand vs Jotai
| Feature | Redux | Zustand | Jotai |
|---|---|---|---|
| Bundle Size | ~40 KB | ~1.2 KB | ~2.8 KB |
| Boilerplate | Alto | Baixo | Médio |
| Learning Curve | Íngreme | Suave | Médio |
| Performance | Boa | Excelente | Excelente |
| DevTools | Sim | Sim | Sim |
| Persistence | Middleware | Built-in | Extensões |
| TypeScript | Complexo | Simples | Excelente |
| Async | Thunks/Saga | Nativo | Nativo |
Quando Usar Cada Um
Use Zustand quando:
✅ Quer simplicidade máxima
✅ State global simples a moderado
✅ Precisa de persistence fácil
✅ Vindo do Context API
✅ Equipe pequena/média
Use Jotai quando:
✅ Prefere atomic state
✅ Precisa de derived state complexo
✅ Suspense e async são importantes
✅ Fine-grained reactivity
✅ Influência do Recoil/atoms
Use Redux quando:
✅ Projeto enterprise grande
✅ Time-travel debugging essencial
✅ Ecossistema Redux necessário
✅ Time já domina Redux
✅ State extremamente complexo
Migrando de Redux
// Antes: Redux
const mapStateToProps = (state) => ({
count: state.counter.count,
user: state.user.data
});
const mapDispatchToProps = {
increment,
fetchUser
};
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
// Depois: Zustand
import { useCounterStore } from './store/counter';
import { useUserStore } from './store/user';
function MyComponent() {
const count = useCounterStore(state => state.count);
const increment = useCounterStore(state => state.increment);
const user = useUserStore(state => state.user);
const fetchUser = useUserStore(state => state.fetchUser);
// Muito mais simples e direto!
}Se você quer entender melhor as tendências modernas de React, confira: Svelte 5 Runes: A Revolução de Reatividade onde exploramos como frameworks modernos estão reimaginando state management.
Bora pra cima! 🦅
💻 Domine JavaScript de Verdade
O conhecimento que você adquiriu neste artigo é só o começo. Há técnicas, padrões e práticas que transformam desenvolvedores iniciantes em profissionais requisitados.
Formas de pagamento:
- R$9,90 (pagamento único)

