Zustand vs Redux Toolkit: gerenciamento de estado que não te odeia

1. O Problema do Estado Global: Por Que a Dor Existe?

Gerenciar estado global em aplicações React sempre foi um campo minado. Durante anos, desenvolvedores aceitaram que lidar com estado centralizado significava enfrentar boilerplate interminável, configurações complexas e uma curva de aprendizado íngreme. O problema não era a ideia de ter um estado global — era como as ferramentas existentes nos obrigavam a implementá-lo.

Em projetos pequenos e médios, a complexidade desnecessária se torna rapidamente um fardo. Você começa com um contador simples e, de repente, está criando actions, reducers, constantes e conectando componentes com mapStateToProps. O trade-off histórico entre boilerplate e produtividade sempre pendia para o lado da burocracia.

O estado global deveria ser uma solução, não mais um problema. Felizmente, o ecossistema React evoluiu, e hoje temos duas abordagens que prometem exatamente isso: Redux Toolkit e Zustand. Ambas nasceram da insatisfação com o status quo, mas seguem filosofias radicalmente diferentes.

2. Redux Toolkit: O Padrão Maduro e Opinionado

Redux Toolkit (RTK) é a resposta oficial da equipe Redux para os anos de reclamações sobre boilerplate. Ele não reinventa a roda — ele a simplifica drasticamente.

Com createSlice, você define estado, reducers e actions em um único bloco de código. O Immer integrado permite que você "mute" o estado diretamente, enquanto o RTK cuida da imutabilidade nos bastidores. Para operações assíncronas, createAsyncThunk elimina a necessidade de escrever action creators manualmente para cada estado (pending, fulfilled, rejected).

// Redux Toolkit - store configurado com slice
import { createSlice, configureStore, createAsyncThunk } from '@reduxjs/toolkit'

const fetchUser = createAsyncThunk('user/fetch', async (userId) => {
  const response = await fetch(`/api/users/${userId}`)
  return response.json()
})

const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false },
  reducers: {
    updateName: (state, action) => {
      state.data.name = action.payload // Immer permite "mutação"
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => { state.loading = true })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false
        state.data = action.payload
      })
  }
})

const store = configureStore({ reducer: { user: userSlice.reducer } })

RTK Query eleva ainda mais o nível, gerenciando cache, polling e invalidação de dados de API de forma declarativa. É uma solução completa para quem precisa de previsibilidade em larga escala.

3. Zustand: Minimalismo que Abraça a Simplicidade

Zustand (alemão para "estado") é a antítese do pensamento opinionado. Sua filosofia é simples: você não precisa de um provider, não precisa de contexto extra, não precisa de dezenas de arquivos. Uma store é um hook.

// Zustand - store completa em poucas linhas
import { create } from 'zustand'

const useStore = create((set, get) => ({
  count: 0,
  user: null,
  loading: false,
  increment: () => set((state) => ({ count: state.count + 1 })),
  fetchUser: async (userId) => {
    set({ loading: true })
    const response = await fetch(`/api/users/${userId}`)
    const data = await response.json()
    set({ user: data, loading: false })
  },
  updateNestedField: (path, value) => {
    const current = get()
    // Lógica para atualização aninhada
    set({ ...current, [path]: value })
  }
}))

A API enxuta — create, set, get — é tudo que você precisa. Middleware como persist, immer e devtools são plugins opcionais que se integram sem configurar boilerplate. Não há contexto, não há providers aninhados, não há ceremony.

4. Comparação Prática: Código Lado a Lado

Vamos comparar uma lista de tarefas simples em ambas as ferramentas.

Redux Toolkit:

const todoSlice = createSlice({
  name: 'todos',
  initialState: { items: [], filter: 'all' },
  reducers: {
    addTodo: (state, action) => {
      state.items.push({ id: Date.now(), text: action.payload, done: false })
    },
    toggleTodo: (state, action) => {
      const todo = state.items.find(t => t.id === action.payload)
      if (todo) todo.done = !todo.done
    },
    setFilter: (state, action) => { state.filter = action.payload }
  }
})
// Componente precisa usar useDispatch e useSelector

Zustand:

const useTodoStore = create((set) => ({
  items: [],
  filter: 'all',
  addTodo: (text) => set((state) => ({
    items: [...state.items, { id: Date.now(), text, done: false }]
  })),
  toggleTodo: (id) => set((state) => ({
    items: state.items.map(t => t.id === id ? { ...t, done: !t.done } : t)
  })),
  setFilter: (filter) => set({ filter })
}))
// Componente usa o hook diretamente: const { items, addTodo } = useTodoStore()

Para estado assíncrono, ambos lidam bem, mas Zustand ganha em simplicidade para casos simples. RTK Query é superior para cenários complexos de cache e polling.

Em atualizações aninhadas, o Immer do RTK brilha — você pode modificar objetos profundos sem espalhar operadores. Zustand exige mais cuidado manual, mas o middleware immer resolve isso.

5. Performance e Bundle Size: Quem Pesa Menos?

Os números não mentem. Redux Toolkit pesa aproximadamente 11KB (gzip), enquanto Zustand fica em torno de 1KB (gzip). Para aplicações onde cada kilobyte importa — como Progressive Web Apps ou sites mobile — Zustand é imbatível.

Em termos de re-renderizações, RTK oferece seletores memoizados com createSelector do Reselect, permitindo controle refinado. Zustand, por padrão, evita re-renderizações desnecessárias ao comparar referências dos valores retornados pelo seletor.

Para debugging, o ecossistema Redux é mais maduro: Redux DevTools são padrão da indústria. Zustand também suporta DevTools via middleware, mas a experiência não é tão rica.

6. Casos de Uso: Quando Escolher Cada Um?

Zustand ganha de lavada em:
- Projetos pequenos e médios (startups, MVPs, ferramentas internas)
- Micro-frontends e estados isolados por módulo
- Aplicações que priorizam bundle size mínimo
- Times pequenos que valorizam produtividade imediata

Redux Toolkit brilha em:
- Grandes aplicações com múltiplos times e dezenas de desenvolvedores
- Cenários que exigem middleware complexo (logging, analytics, sagas)
- Aplicações com estado altamente compartilhado entre domínios distantes
- Projetos que já usam Redux clássico e querem migrar gradualmente

7. Migração e Convivência: Os Dois Podem Coexistir?

Sim, e isso é um dos pontos fortes de ambas as bibliotecas. Você pode usar Zustand para estado de UI (modais, temas, sidebar) enquanto mantém Redux Toolkit para dados de domínio (usuários, produtos, pedidos).

// Coexistência pacífica
import { useDispatch } from 'react-redux'
import { useUIStore } from './stores/uiStore'

function Component() {
  const dispatch = useDispatch()
  const { theme, toggleSidebar } = useUIStore()

  // Redux para dados de domínio
  const handleSave = () => dispatch(saveUserData())
  // Zustand para estado de UI
  const handleToggle = () => toggleSidebar()
}

Estratégias de migração gradual incluem: começar novos recursos com Zustand, mover estado de UI do Redux para Zustand, ou usar Zustand como camada de cache local enquanto RTK Query gerencia o servidor.

8. Conclusão: A Escolha que Não Te Odeia

Característica Redux Toolkit Zustand
Bundle size (gzip) ~11KB ~1KB
Curva de aprendizado Moderada Baixa
Boilerplate Baixo (para RTK) Mínimo
Ecossistema Maduro Crescente
Ideal para Grandes apps, times grandes Projetos médios, MVPs
Debugging DevTools nativos DevTools via middleware

A escolha final depende do fator humano: a familiaridade da sua equipe, a complexidade do projeto e a necessidade de previsibilidade vs. velocidade. Minha recomendação pessoal: comece com Zustand. Ele resolve 80% dos problemas de estado com 20% do esforço. Quando você sentir dor — quando precisar de middleware complexo, cache sofisticado ou rastreamento de ações impecável — evolua para Redux Toolkit.

O importante é que nenhuma das duas ferramentas te odeia. Ambas foram criadas por desenvolvedores que sentiram sua dor e decidiram fazer algo a respeito. Escolha a que faz seu time mais feliz e produtivo.

Referências