R
Interview Deckreact · next.js · senior/staff
0·0·122
Estado Global

Context API — Padrões e Limitações

Context para dados estáveis, splitting por frequência, e o padrão Provider + hook customizado.

Context API é nativa do React — sem dependências externas. Ideal para dados que: mudam raramente (tema, idioma, usuário logado), ou precisam ser acessados em muitos níveis de profundidade sem prop drilling. Limitação crítica: qualquer mudança no value re-renderiza TODOS os consumers, mesmo que não usem o valor que mudou.

Problema
Context com objeto grande onde qualquer campo muda frequentemente: todos os consumers re-renderizam a cada mudança. Difícil otimizar sem usar libs extras. Acesso direto ao Context espalhado no código em vez de hooks customizados.
Solução
Splitting: um Context por frequência de atualização (AuthContext separado de ThemeContext). Memoizar value com useMemo. Sempre expor via hook customizado (useAuth()) em vez de useContext(AuthContext) direto — facilita troca de implementação. Para estado frequente, Zustand.
Tip
O padrão correto: Context armazena o valor, hook customizado encapsula o acesso. `export function useTheme() { return useContext(ThemeContext) }`. Isso permite trocar a implementação interna sem mudar todos os consumers.
// Padrão: Context + hook customizado
interface AuthContextValue {
  user: User | null
  login: (credentials: Credentials) => Promise<void>
  logout: () => void
}

const AuthContext = createContext<AuthContextValue | null>(null)

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null)

  const login = useCallback(async (credentials) => {
    const user = await authApi.login(credentials)
    setUser(user)
  }, [])

  const logout = useCallback(() => setUser(null), [])

  // useMemo evita novo objeto a cada render
  const value = useMemo(
    () => ({ user, login, logout }),
    [user, login, logout]
  )

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

// Hook customizado — nunca expor o Context diretamente
export function useAuth() {
  const ctx = useContext(AuthContext)
  if (!ctx) throw new Error('useAuth deve ser usado dentro de AuthProvider')
  return ctx
}

// Context splitting — frequências separadas
<ThemeProvider>      {/* muda raramente */}
  <AuthProvider>     {/* muda ao login/logout */}
    <UIProvider>     {/* muda por interação */}
      <App />
    </UIProvider>
  </AuthProvider>
</ThemeProvider>
Q.Como otimizar Context para evitar re-renders?
A.Splitting: separar contextos por frequência (AuthContext, ThemeContext, UIContext). Memoizar o value: `const value = useMemo(() => ({ user, logout }), [user])`. Para seletores (só ouvir parte do context), usar use-context-selector lib. Mas se o estado muda frequentemente, Zustand é a solução certa.
Q.Context API pode substituir Redux?
A.Para estado simples e raramente atualizado, sim. Para estado complexo com muitas atualizações, não — sem o conceito de seletor, todos os consumers re-renderizam. Context não tem devtools, middleware, ou estrutura para mutations complexas. Use Context para DI (dependency injection) de valores estáveis, Redux/Zustand para estado dinâmico.
Q.O que é o padrão Compound + Context?
A.Compound Components que compartilham estado via Context: o componente pai cria um Context privado, filhos consomem. Ex: <Accordion> cria um Context com {openIndex, setOpenIndex}. <Accordion.Item> consome via useContext interno. Usuário não precisa gerenciar estado — a API é declarativa.
69/122← →