State Management Moderno en 2025: Por Qué Zustand y Jotai Están Superando a Redux
Hola HaWkers, durante años Redux fue el estándar de oro para gestión de estado en React. Pero en 2025, desarrolladores están abandonando Redux en masa por soluciones más simples y modernas.
Zustand y Jotai representan una nueva generación de state management: menos boilerplate, mejor performance, y developer experience superior.
El Problema con Redux
// Redux - Mucho boilerplate para algo simple
// 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 archivos, ~60 líneas para un contador! 😱
Zustand: Simplicidad Extrema
// store/counter.js - ¡TODO el state management en un archivo!
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 archivos, ~20 líneas. ¡Mucho más simple! ✨Zustand Features Avanzadas
// store/user.js - Store completa con 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 con Async Data
// atoms/users.js
import { atom } from 'jotai';
// Async atom - busca datos automáticamente
export const usersAtom = atom(async () => {
const response = await fetch('/api/users');
return response.json();
});
// Atom con 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>Cargando usuarios...</div>}>
<UserList />
</Suspense>
);
}
Comparación: Redux vs Zustand vs Jotai
| Feature | Redux | Zustand | Jotai |
|---|---|---|---|
| Bundle Size | ~40 KB | ~1.2 KB | ~2.8 KB |
| Boilerplate | Alto | Bajo | Medio |
| Learning Curve | Empinada | Suave | Medio |
| Performance | Buena | Excelente | Excelente |
| DevTools | Sí | Sí | Sí |
| Persistence | Middleware | Built-in | Extensiones |
| TypeScript | Complejo | Simple | Excelente |
| Async | Thunks/Saga | Nativo | Nativo |
Cuándo Usar Cada Uno
Usa Zustand cuando:
✅ Quieres simplicidad máxima
✅ State global simple a moderado
✅ Necesitas persistence fácil
✅ Vienes de Context API
✅ Equipo pequeño/medio
Usa Jotai cuando:
✅ Prefieres atomic state
✅ Necesitas derived state complejo
✅ Suspense y async son importantes
✅ Fine-grained reactivity
✅ Influencia de Recoil/atoms
Usa Redux cuando:
✅ Proyecto enterprise grande
✅ Time-travel debugging esencial
✅ Ecosistema Redux necesario
✅ Team ya domina Redux
✅ State extremamente complejo
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);
// Después: 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);
// ¡Mucho más simple y directo!
}Si quieres entender mejor las tendencias modernas de React, confiere: Svelte 5 Runes: La Revolución de Reactividad donde exploramos cómo frameworks modernos están reimaginando state management.
¡Vamos a por ello! 🦅
Domina JavaScript de Verdad
El conocimiento que adquiriste en este artículo es solo el comienzo. Hay técnicas, patrones y prácticas que transforman desarrolladores principiantes en profesionales requisitados.
Formas de pago:
- $9.90 USD (pago único)

