Voltar para o Blog

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)

📖 Ver Conteúdo Completo

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário