Menu

Prop drilling no React: o que é e como evitar

Prop drilling é passar uma prop por componentes que não a usam, só para que um componente mais abaixo possa usá-la. Veja isso em um exemplo executável, por que atrapalha quando o app cresce e as soluções na ordem em que vale tentar: composição, context e depois uma biblioteca de estado.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Prop drilling é passar uma prop por componentes que não a usam, só para que um componente mais abaixo na árvore possa usá-la. Aqui user viaja de App por Page e Sidebar até chegar a Avatar, o único componente que o lê.

Quatro níveis, e só o último se importa com user. O Console mostra Page e Sidebar executando por causa de um valor que eles nunca exibem. Clique no botão e eles registram de novo.

Por que atrapalha

Dois ou três níveis não são problema. O prop drilling vira um problema conforme a árvore cresce:

  • Os componentes do meio carregam props que não usam. Page e Sidebar agora têm uma prop user na assinatura, e você não consegue lê-los sem se perguntar para que ela serve.
  • Cada mudança mexe em cada camada. Adicione user.avatarUrl ou um segundo valor como onLogout e você edita cada componente do caminho, não só as duas pontas.
  • Mover um componente quebra a corrente. Mova Avatar para outro ramo e você precisa passar user por um caminho novo.
  • É fácil perder uma prop. Esqueça de repassá-la em uma camada e o componente profundo recebe undefined, sem erro na camada que a perdeu.

Experimente: adicione em App uma função onLogout que Avatar deva chamar. Você vai editar os quatro componentes.

Solução 1: composição com children

Esta é a solução que as pessoas pulam. Em vez de passar dados para baixo para que um componente profundo renderize com eles, deixe o componente dono dos dados renderizar ele mesmo o componente profundo e passe o elemento pronto para baixo como children (ou qualquer outra prop). As camadas do meio renderizam um espaço e nunca veem user.

Page e Sidebar não sabem mais que existe um usuário. App cria Avatar onde user mora e o entrega para baixo como um elemento pronto. Componentes de layout como esses ficam reutilizáveis, e adicionar onLogout agora é uma mudança só em App e Avatar. Os dois ainda registram a cada clique, porque App passa um elemento novo a cada renderização: a composição simplifica o código, não o número de renderizações. A página sobre children mostra mais padrões de slots.

A composição funciona quando o componente dono dos dados também pode decidir como fica a parte profunda. Ela não ajuda quando o componente profundo está dentro de uma biblioteca ou de uma estrutura que o dono não renderiza.

Solução 2: context

Quando muitos componentes em profundidades diferentes precisam do mesmo valor (o tema, o usuário logado, o idioma atual), coloque-o em um context. Qualquer componente abaixo do provider o lê com useContext, e nada no meio muda.

Avatar e Greeting leem o usuário a partir de duas profundidades diferentes, e Page e Sidebar não recebem props. Adicione um terceiro leitor em qualquer lugar abaixo de Page e ele funciona sem mexer no resto. O custo: o fluxo de dados deixa de ficar visível nas props, e todo leitor renderiza quando o valor muda. A página do useContext trata de providers, valores padrão e dessas re-renderizações.

Solução 3: uma biblioteca de estado

Para um estado grande do app que muitos componentes leem e que muda com frequência (um carrinho, um editor de documentos, dados ao vivo), uma biblioteca de estado como Redux Toolkit, Zustand ou Jotai permite que cada componente assine só a fatia que usa. Isso evita tanto o drilling quanto o custo de "todo consumidor renderiza" de um grande context. Também é mais uma dependência e mais um conjunto de conceitos, então recorra a ela quando as duas primeiras soluções deixarem de bastar, não para resolver uma prop que viaja três níveis.

// Zustand, for comparison (not available in the editor here)
const useCart = create((set) => ({
    items: [],
    add: (item) => set((s) => ({ items: [...s.items, item] })),
}));

function CartCount() {
    const count = useCart((s) => s.items.length); // renders only when the count changes
    return <span>{count}</span>;
}

Quando o prop drilling não é problema

Passar props é como os dados fluem no React, e é o padrão mais fácil de ler: você acompanha um valor de cima a baixo olhando o JSX. Uma prop que passa por um ou dois componentes que usam parte dela, ou por uma cadeia curta de componentes bem relacionados, não é um problema a resolver. Eleve o estado para o pai comum mais próximo, passe-o para baixo e só mude de abordagem quando as camadas do meio começarem a carregar props que não usam para nada.

Perguntas frequentes

O que é prop drilling no React?

Passar uma prop por várias camadas de componentes que não a usam, só para que um componente no fundo da árvore possa lê-la. Cada componente do meio aceita a prop e a repassa.

Prop drilling é ruim?

Não por si só. Passar um valor dois ou três níveis para baixo é explícito e fácil de acompanhar. Vira um problema quando muitas camadas carregam props que nunca usam, e assim toda renomeação ou campo novo mexe em arquivos que não têm nada a ver com isso.

Como evito o prop drilling?

Tente primeiro a composição: deixe o pai montar o componente profundo e passá-lo para baixo como children ou outra prop, para que as camadas do meio nunca vejam os dados. Se muitos componentes em profundidades diferentes precisam do valor, use context. Para estado grande que muda com frequência, considere uma biblioteca de estado.

O context é a única solução para o prop drilling?

Não, e muitas vezes não é a melhor. A composição com children elimina o drilling sem adicionar um context e mantém o fluxo de dados visível no código.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR