Boas Práticas de Gerenciamento de Estado em React: Context API e Zustand para Times Ágeis Já leu

Context API: Fundamentos e Implementação A Context API é nativa do React e permite compartilhar estado entre componentes sem prop drilling. Ela cria um contexto que armazena dados e os disponibiliza para qualquer componente descendente que o consuma. Para iniciantes, é a solução ideal porque não requer dependências externas. Vamos criar um exemplo prático com um tema de autenticação: Agora use no seu App: Limitações da Context API e Quando Evitar A Context API é poderosa, mas tem limitações importantes. Qualquer mudança no contexto causa re-render de todos os componentes que o consomem, mesmo que usem apenas parte dos dados. Em aplicações com estado complexo e frequentes atualizações, isso impacta performance. Além disso, não há ferramentas nativas para debugging ou time-travel. Para aplicações pequenas ou médias com estado simples, a Context API é perfeita. Mas quando o estado é complexo, com múltiplas ações e atualizações frequentes, considere alternativas como Zustand. A regra prática: se seu contexto mudar mais de 10

Context API: Fundamentos e Implementação

A Context API é nativa do React e permite compartilhar estado entre componentes sem prop drilling. Ela cria um contexto que armazena dados e os disponibiliza para qualquer componente descendente que o consuma. Para iniciantes, é a solução ideal porque não requer dependências externas.

Vamos criar um exemplo prático com um tema de autenticação:

import React, { createContext, useState, useContext } from 'react';

// 1. Criar o contexto
const AuthContext = createContext();

// 2. Criar o provider (gerenciador de estado)
export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);

  const login = async (email, password) => {
    setLoading(true);
    try {
      // Simulando chamada à API
      await new Promise(resolve => setTimeout(resolve, 1000));
      setUser({ email, id: Math.random() });
    } finally {
      setLoading(false);
    }
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, loading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// 3. Hook customizado para usar o contexto
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth deve ser usado dentro de AuthProvider');
  }
  return context;
}

Agora use no seu App:

function App() {
  return (
    <AuthProvider>
      <Dashboard />
    </AuthProvider>
  );
}

function Dashboard() {
  const { user, login, logout, loading } = useAuth();

  if (!user) {
    return (
      <button onClick={() => login('user@example.com', '123')}>
        {loading ? 'Autenticando...' : 'Login'}
      </button>
    );
  }

  return (
    <div>
      <p>Bem-vindo, {user.email}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Limitações da Context API e Quando Evitar

A Context API é poderosa, mas tem limitações importantes. Qualquer mudança no contexto causa re-render de todos os componentes que o consomem, mesmo que usem apenas parte dos dados. Em aplicações com estado complexo e frequentes atualizações, isso impacta performance. Além disso, não há ferramentas nativas para debugging ou time-travel.

Para aplicações pequenas ou médias com estado simples, a Context API é perfeita. Mas quando o estado é complexo, com múltiplas ações e atualizações frequentes, considere alternativas como Zustand. A regra prática: se seu contexto mudar mais de 10 vezes por segundo ou gerencia mais de 5 propriedades interdependentes, provavelmente você precisa de Zustand.

Zustand: Estado Simples e Performático

Zustand é uma biblioteca minimalista (2KB gzipped) que oferece gerenciamento de estado sem o overhead da Context API. Ela usa Hooks nativos do React e permite criar stores de forma simples e direta. Zustand só re-renderiza componentes quando a parte específica do estado que eles usam muda.

Vamos recriar o exemplo de autenticação com Zustand:

import { create } from 'zustand';

const useAuthStore = create((set) => ({
  user: null,
  loading: false,

  login: async (email, password) => {
    set({ loading: true });
    try {
      await new Promise(resolve => setTimeout(resolve, 1000));
      set({ user: { email, id: Math.random() } });
    } finally {
      set({ loading: false });
    }
  },

  logout: () => {
    set({ user: null });
  }
}));

// Componente consumidor
function Dashboard() {
  const user = useAuthStore((state) => state.user);
  const login = useAuthStore((state) => state.login);
  const logout = useAuthStore((state) => state.logout);
  const loading = useAuthStore((state) => state.loading);

  if (!user) {
    return (
      <button onClick={() => login('user@example.com', '123')}>
        {loading ? 'Autenticando...' : 'Login'}
      </button>
    );
  }

  return (
    <div>
      <p>Bem-vindo, {user.email}</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Otimizações Avançadas com Zustand

Para máxima performance, use seletores granulares. No exemplo acima, cada linha useAuthStore((state) => state.propriedade) seleciona apenas uma propriedade. Isso garante que o componente só re-renderize quando aquela propriedade específica mudar.

// Antes (renderiza em qualquer mudança do store)
const auth = useAuthStore();

// Depois (renderiza apenas quando user mudar)
const user = useAuthStore((state) => state.user);

// Ou use a função shallowEqual para múltiplas propriedades
import { shallow } from 'zustand/react/shallow';
const { user, loading } = useAuthStore(
  (state) => ({ user: state.user, loading: state.loading }),
  shallow
);

Zustand também oferece middleware para persistência e devtools:

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

const useAuthStore = create(
  devtools(
    persist(
      (set) => ({
        user: null,
        logout: () => set({ user: null }),
      }),
      { name: 'auth-store' }
    )
  )
);

Comparação Prática: Quando Usar Cada Uma

Aspecto Context API Zustand
Dependência externa Não Sim (2KB)
Curva de aprendizado Mais suave Muito rápida
Performance em grandes stores Pode sofrer Excelente
DevTools nativas Não Sim, com middleware
Ideal para Estado simples, múltiplos contextos Estado complexo e centralizado

Use Context API para: temas, idiomas, autenticação simples, preferências do usuário que mudam raramente. Use Zustand para: carrinho de compras, estados de formulários complexos, cache de dados, qualquer estado que mude frequentemente.

Um exemplo real: um e-commerce com contexto de tema (Context API) e store de carrinho (Zustand):

// Tema simples com Context API
const ThemeContext = createContext();

// Carrinho complexo com Zustand
const useCartStore = create((set) => ({
  items: [],
  addItem: (product) => set((state) => ({
    items: [...state.items, product]
  })),
  removeItem: (id) => set((state) => ({
    items: state.items.filter(item => item.id !== id)
  })),
  total: () => // lógica complexa
}));

Conclusão

A Context API é sua porta de entrada para gerenciamento de estado em React — simples, sem dependências, perfeita para estado compartilhado que muda ocasionalmente. A Zustand é o upgrade natural quando a complexidade crescer: menor overhead, melhor performance, ferramentas robustas.

O segredo é começar simples. Use Context API, aprenda seus limites, e migre para Zustand quando a necessidade aparecer. Ambas coexistem bem em um projeto real: use Context para configurações globais e Zustand para estado de aplicação dinâmico. Não existe "melhor" — existe o certo para cada caso.

Referências


Artigos relacionados