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.