Todo desenvolvedor React acaba encontrando o mesmo obstáculo. Você busca um objeto de usuário dentro do seu componente App de nível superior. Então, você o passa para baixo. E para baixo novamente. Através de um wrapper de rota, através de um shell de layout, através de um container de sidebar, apenas para que um pequeno componente de avatar, três camadas abaixo, possa exibir uma foto de perfil. Os componentes no meio não se importam com esse objeto de usuário. Eles estão apenas encaminhando o pacote. Isso é o prop drilling, e ele transforma uma árvore de componentes limpa em um frustrante jogo de telefone sem fio.
A verdadeira dor começa quando o formato desses dados muda. Talvez o backend comece a aninhar user.profile.avatar em vez de user.avatar. De repente, você está editando interfaces TypeScript ou PropTypes em cinco arquivos que nunca utilizam os dados em si. É aí que o React Context API entra em cena.
Como o Context redefine o fluxo de dados
Pense no Context como um roteador WiFi posicionado no centro da sua casa. Sem ele, você precisaria de cabos Ethernet serpenteando por todos os cômodos para levar um sinal ao seu laptop. Com ele, o roteador transmite pelo ar, e qualquer dispositivo com a senha correta pode se conectar diretamente. As paredes não importam.
Em termos de React, a raiz do seu app pode transmitir dados através da árvore de componentes sem pedir que cada camada atue como um mensageiro. Qualquer componente aninhado pode assinar essa transmissão e receber exatamente o que precisa.
As Três Peças Fundamentais
A Context API resume-se a três partes móveis.
React.createContext() configura o canal de transmissão. Ele retorna um objeto contendo um Provider e (em códigos mais antigos) um Consumer. Você só precisa chamar isso uma vez para uma determinada funcionalidade.
The Provider é um componente que envolve uma seção da sua árvore. Ele aceita uma prop chamada value. Tudo o que você colocar nessa prop ficará disponível para todos os descendentes, não importa o quão profundos eles estejam.
useContext é o Hook que permite que um componente de função se conecte a essa transmissão. Dentro do seu componente, você passa o objeto de contexto que criou para o useContext, e ele retorna o valor atual. É só isso. Sem wrappers, sem props extras.
Antes da chegada dos Hooks, você tinha que usar o padrão Consumer com render props. Funcionava, mas criava muita indentação e poluição de wrappers. O useContext simplificou tudo isso em uma única linha dentro do corpo da sua função.
Quando o Context realmente faz sentido
Não recorra ao Context por hábito. Ele foi feito para dados que muitos componentes não relacionados compartilham através de diferentes ramos da sua árvore. Bons candidatos incluem:
- Configurações de tema. Não apenas o modo claro ou escuro, mas tokens de espaçamento, paletas de cores e escalas de fonte. Passar isso manualmente por cada botão estilizado e modal cansa rápido.
- Autenticação de usuário. Status de login, um array de permissões ou o objeto do usuário atual. Sua barra de cabeçalho, um widget de dashboard e um guard de rota privada podem estar em cantos diferentes da árvore.
- Preferências de idioma. Strings de localidade, formatos de data e símbolos de moeda. Componentes de folhas profundas, como rótulos de formulário, precisam disso sem que cada pai no caminho precise saber sobre eles.
- Dados do carrinho de compras. Contagem de itens, valor total e funções de adicionar ao carrinho. O badge do cabeçalho e a página de checkout precisam do mesmo estado, mas geralmente ficam sob ramos de layout completamente diferentes.
Um Alternador de Tema Prático
Uma das formas mais claras de ver o Context em ação é um alternador de tema (theme toggle). Veja como você poderia configurá-lo sem pular os detalhes que realmente importam.
Primeiro, crie um arquivo ThemeContext.js. Chame React.createContext() e armazene o resultado. Em seguida, construa um componente ThemeProvider que gerencie o tema atual com useState ou useReducer. Envolva os filhos no Provider do seu contexto, passando um objeto que contenha tanto o tema atual quanto uma função para alterná-lo. Exporte tanto o ThemeProvider quanto o próprio objeto de contexto.
Segundo, vá para o ponto de entrada do seu app. Importe o ThemeProvider e envolva toda a sua aplicação com ele. Se você pular esta etapa, qualquer coisa que tente ler o contexto mais tarde verá apenas o valor padrão.
Terceiro, dentro de um componente Header ou Content, importe o objeto de contexto e o useContext. Chame o Hook, desestruture o tema e a função de alternância, e aplique suas classes CSS condicionalmente. Adicione um botão que chame a função de alternância. O componente nunca recebe uma prop theme de seu pai. Ele capta o sinal diretamente do ar.
Prop Drilling, Context ou Redux?
Escolher entre essas ferramentas tem menos a ver com lealdade e mais com o formato do seu estado.
Prop drilling é perfeitamente aceitável para duas ou três camadas de profundidade. É explícito, fácil de rastrear no seu IDE e mantém as dependências óbvias. Os problemas só aparecem quando você começa a passar a mesma prop por seis ou sete camadas.
A Context API já vem com o próprio React. Isso significa que não há aumento no tamanho do bundle nem configuração externa. Ela lida muito bem com estados globais de pequeno a médio porte, especialmente dados que mudam com pouca frequência, como temas ou perfis de usuário.
O Redux exige a instalação de bibliotecas adicionais e a escrita de boilerplate. Ele compensa quando a lógica do seu estado é complexa, quando múltiplas fatias (slices) de estado interagem de formas profundas ou quando você precisa de time-travel debugging e middleware. Para dados globais simples, o Redux é um exagero.
A Realidade de Performance de que Ninguém Fala
Aqui está o detalhe que separa implementações de nível júnior de nível sênior. Quando o valor de um Context Provider muda, cada componente que consome esse contexto é renderizado novamente. Não importa se a fatia específica da qual aquele componente precisa permaneceu a mesma. O React vê a nova referência e agenda uma atualização.
Se você despejar todo o estado da sua aplicação em um único StoreContext gigante, você terá, na prática, colado toda a sua UI. Alterar uma configuração de tema irá renderizar novamente seu carrinho de compras, seus gráficos do dashboard e sua lista de notificações. Isso é trabalho desnecessário.
Divida seus contextos por domínio. Mantenha um ThemeContext para configurações visuais, um UserContext para dados de perfil e um CartContext para o estado de comércio. Se um usuário editar seu nome de exibição, seu cabeçalho será atualizado sem tocar na grade de produtos. Além disso, tenha cuidado com o que você passa na prop value do Provider. Se você passar um objeto literal { theme, toggleTheme } inline durante a renderização, você criará uma nova referência a cada render e disparará atualizações desnecessárias. Estabilize essa estrutura com useMemo se o valor contiver funções ou dados não primitivos.
Erros que Consomem Horas
Dois erros pegam as equipes repetidamente.
Esquecer de exportar o objeto de contexto. É fácil exportar o componente ThemeProvider e depois tentar chamar useContext(ThemeProvider). Não é assim que funciona. O Hook precisa do objeto de contexto retornado por createContext, não do componente wrapper. Se você exportar apenas o Provider, seus consumidores não terão nada para importar.
Chamar useContext fora do seu Provider. O Hook retorna o valor padrão que você passou para o createContext. Se você não passou um valor padrão, receberá undefined. Se a sua árvore de componentes renderizar o consumidor em um nível mais alto no DOM do que o Provider, ou se o Provider estiver faltando por completo, seus dados simplesmente não chegarão. Verifique novamente se o seu arquivo index ou root realmente envolve a aplicação.
A Principal Lição
O React Context não é uma revolução no gerenciamento de estado. É uma ferramenta direcionada para um problema espacial específico: levar dados a componentes distantes sem transformar cada camada em um correio. Use-o para dados genuinamente globais, mantenha seus contextos divididos por domínio para proteger a performance de renderização e sempre envolva sua árvore com o Provider correto antes de tentar ler o sinal. Domine esses hábitos e suas árvores de componentes permanecerão limpas, rápidas e fáceis de entender.
