Context: o que resolve, e o render que provoca sem se dar por ele
O Context resolve um problema: entregar um valor a um componente fundo na árvore sem o passar à mão por todos os que estão pelo caminho. Não é mais do que isso — e sobretudo não é gestão de estado. É dessa confusão que vem quase tudo o que corre mal com ele.
O estado continua a viver num useState acima. O Context é só o túnel.
1. O render que ele provoca
Quando o valor do Context muda, todos os componentes que o leem voltam a renderizar. Não os que usam a parte que mudou: todos. O React não tem como saber que pedaço do valor interessa a cada um — para ele o valor é um só, e comparou-o por referência.
E o memo não salva ninguém:
const Avatar = memo(function Avatar() {
const { utilizador } = useContext(ContextoSessao);
return <img src={utilizador.foto} />;
});Aquele memo compara props, e o valor do Context não chega como prop — entra por dentro. O componente volta a renderizar sempre que o Context mudar, com memo ou sem ele. É um dos casos em que ele é só ruído, como no artigo do useMemo e do useCallback.
2. A armadilha do objeto novo
// A cada render deste componente nasce um objeto novo
<ContextoSessao.Provider value={{ utilizador, entrar, sair }}>O utilizador pode ser exactamente o mesmo, e entrar e sair podem fazer exactamente o que faziam. O objeto que os embrulha é que é novo — e é ele que o React compara. Resultado: o Context «mudou», e todos os consumidores renderizam por nada.
A correção é dar estabilidade ao valor:
const valor = useMemo(
() => ({ utilizador, entrar, sair }),
[utilizador, entrar, sair],
);Com uma ressalva que se percebe mal: isto só ajuda quando o provider renderiza por outros motivos. Se foi o utilizador que mudou, os consumidores renderizam — e devem. O useMemo não evita renders necessários; evita os que não eram.
3. Dividir em dois
A divisão que resolve o problema a sério não é um useMemo: é separar o que muda do que não muda.
<ContextoUtilizador.Provider value={utilizador}> {/* muda a cada login */}
<ContextoAcoes.Provider value={acoes}> {/* nasce uma vez */}
{filhos}
</ContextoAcoes.Provider>
</ContextoUtilizador.Provider>Um botão de «sair» só precisa da ação. Lendo apenas o segundo Context, nunca volta a renderizar quando o utilizador muda — porque o que ele lê não mudou. É o mesmo raciocínio das chaves estáveis: o que importa é o React conseguir distinguir o que é o mesmo do que é outro.
4. O caso deste site: o tema, sem Context nenhum
Este site não tem um único createContext. Não é posição de princípio — é que o sítio onde ele era óbvio acabou resolvido de outra maneira, e as razões valem mais do que a regra.
O alternador de tema é o exemplo de manual do Context. Só que aqui a verdade sobre o tema não está no React: está numa classe no <html>, posta por um guião em linha que corre antes de a página ser pintada. Sem isso, o site desenha-se claro e só depois escurece — um clarão branco a cada navegação, precisamente a quem escolheu o escuro.
Com um Context passava a haver duas verdades sobre a mesma coisa: a classe no <html> e o estado do React. Duas verdades divergem sempre, e divergem em silêncio.
const escuro = useSyncExternalStore(
subscrever,
estaEscuroNoCliente,
estaEscuroNoServidor,
);O useSyncExternalStore é o que serve para ler uma fonte de verdade que vive fora da árvore, em vez de a copiar para dentro. O subscrever observa a classe com um MutationObserver, e por isso o botão acerta mesmo quando o tema é mudado noutro separador.
Há ainda uma terceira razão, a que mais pesa num projeto com App Router: um provider obriga a 'use client'. O Context não atravessa a fronteira entre o servidor e o cliente, e pô-lo no topo arrasta para o browser a árvore toda que ele embrulha — exactamente o contrário do que se foi lá buscar, como no artigo dos Server Components.
5. Duas coisas que o 19 mudou
O provider passou a ser o próprio Context:
<Contexto value={...}>em vez de<Contexto.Provider value={...}>.use(Contexto)pode ser lido dentro de umif, o que ouseContextnunca permitiu.
Ambas ficaram de fora do artigo da migração do 18 para o 19 por serem pequenas, e são das poucas mudanças daquela versão que não partem nada.
O resumo
O Context serve para não passar um valor à mão por dez componentes. Enquanto o valor mudar pouco — o tema, o idioma, a sessão — é a ferramenta certa. A partir do momento em que muda a cada tecla, está-se a pedir à árvore inteira que renderize por causa de uma letra, e o problema deixou de ser de Context.
E antes de criar um: ver se a verdade não está já fora do React. Se estiver, o que falta não é um Context — é uma forma de a ler.