DevPath · Aprenda a programar ESPTEN

Dados e estado global

Estado global: além do Context

Lembrete: Context

useContext resolve o prop drilling: em vez de passar uma prop por dez níveis, um Provider oferece um valor e qualquer descendente o lê. É perfeito para dados que mudam pouco: o tema (claro/escuro), o idioma, o usuário autenticado.

Onde o Context fica aquém

O Context não foi projetado como gerenciador de estado de alto desempenho. Sua limitação principal são os re-renders:

Quando o valor de um Provider muda, todos os componentes que consomem esse contexto são re-renderizados, mesmo que só lhes interesse uma parte do valor.

Se você coloca em um único contexto um objeto grande ({ usuario, carrinho, ajustes, notificacoes }) e muda uma propriedade, todos os consumidores são repintados. O Context não tem seletores: você não pode se inscrever só em carrinho.total e ignorar o resto. Em um app com estado que muda com frequência, isso se traduz em renders desnecessários e a solução na mão (dividir em muitos contextos, memoizar) fica complicada.

Aí entram as bibliotecas de estado global, cuja grande vantagem são os seletores: cada componente se inscreve só no pedaço de estado que usa.

Zustand

Zustand é minimalista. Você cria um store com create passando uma função que define estado e ações:

import { create } from "zustand";

const useContador = create((set) => ({
  cuenta: 0,
  incrementar: () => set((s) => ({ cuenta: s.cuenta + 1 })),
  reset: () => set({ cuenta: 0 }),
}));

O próprio store é um hook. Você o usa com um seletor para ler só o que precisa:

function Placar() {
  // Re-renderiza SÓ se 'cuenta' mudar
  const cuenta = useContador((s) => s.cuenta);
  return <p>Contagem: {cuenta}</p>;
}

function Botao() {
  // Este só lê a ação: não re-renderiza quando 'cuenta' muda
  const incrementar = useContador((s) => s.incrementar);
  return <button onClick={incrementar}>+1</button>;
}

Não precisa de Provider envolvendo o app, não há boilerplate e o seletor (s) => s.cuenta evita os re-renders que o Context sofreria.

Redux Toolkit

Redux é o clássico do estado global previsível: um único store, o estado só muda despachando ações que passam por reducers puros. Redux Toolkit (RTK) é a forma oficial e moderna de usá-lo, e reduz muitíssimo o código repetitivo graças aos slices:

import { configureStore, createSlice } from "@reduxjs/toolkit";

const contadorSlice = createSlice({
  name: "contador",
  initialState: { cuenta: 0 },
  reducers: {
    incrementar: (state) => { state.cuenta += 1; }, // Immer: você "muta" um rascunho
    reset: (state) => { state.cuenta = 0; },
  },
});

export const { incrementar, reset } = contadorSlice.actions;

export const store = configureStore({
  reducer: { contador: contadorSlice.reducer },
});

Nos componentes você lê com useSelector (com seu seletor, como no Zustand) e despacha ações com useDispatch:

import { useSelector, useDispatch } from "react-redux";

function Placar() {
  const cuenta = useSelector((s) => s.contador.cuenta);
  const dispatch = useDispatch();
  return (
    <button onClick={() => dispatch(incrementar())}>
      Contagem: {cuenta}
    </button>
  );
}

Dentro de um reducer do RTK parece que você muta o estado (state.cuenta += 1), mas por baixo ele usa Immer para produzir um novo estado imutável de forma segura.

Qual escolher?

E lembre-se: para dados do servidor, nenhum destes; use TanStack Query ou SWR. O estado global é para o estado do cliente.

(Zustand e Redux são instalados como dependências; aqui os vemos de forma conceitual.)

Coloque isto em prática

O DevPath é um curso prático: aqui você lê a teoria; no app você a coloca em prática com exercícios que rodam de verdade, offline.

Comece grátis no app →
← Data fetching moderno: TanStack Query e SWRYou Might Not Need an Effect →