Passando Dados Profundamente com Contexto
Normalmente, você passa informações de um componente pai para um componente filho via props. Mas passar props pode se tornar verboso e inconveniente se você tiver que passá-las por muitos componentes intermediários, ou se muitos componentes no seu aplicativo precisam da mesma informação.Contextopermite que o componente pai torne algumas informações disponíveis para qualquer componente na árvore abaixo dele—não importa quão profundo—sem passá-las explicitamente através de props.
Você aprenderá
- O que é "prop drilling"
- Como substituir a passagem repetitiva de props por contexto
- Casos de uso comuns para contexto
- Alternativas comuns ao contexto
O problema de passar props
Passar propsé uma ótima maneira de canalizar dados explicitamente através da sua árvore de UI para os componentes que os usam.
Mas passar props pode se tornar verboso e inconveniente quando você precisa passar alguma prop profundamente pela árvore, ou se muitos componentes precisam da mesma prop. O ancestral comum mais próximo pode estar muito distante dos componentes que precisam dos dados, eelevar o estadotão alto pode levar a uma situação chamada "prop drilling".
Elevando o estado


Prop drilling


Não seria ótimo se houvesse uma maneira de "teletransportar" dados para os componentes na árvore que precisam deles sem passar props? Com o recurso de contexto do React, há!
Contexto: uma alternativa a passar props
Contexto permite que um componente pai forneça dados para toda a árvore abaixo dele. Há muitos usos para contexto. Aqui está um exemplo. Considere este componenteHeadingque aceita umlevelpara seu tamanho:
Digamos que você queira que vários títulos dentro da mesmaSectiontenham sempre o mesmo tamanho:
Atualmente, você passa a proplevelpara cada<Heading>separadamente:
Seria bom se você pudesse passar a proplevelpara o componente<Section>em vez disso e removê-la do<Heading>. Dessa forma, você poderia garantir que todos os títulos na mesma seção tenham o mesmo tamanho:
Mas como o componente<Heading>pode saber o nível da sua<Section>mais próxima?Isso exigiria alguma forma de um componente filho "perguntar" por dados de algum lugar acima na árvore.
Você não pode fazer isso apenas com props. É aqui que o contexto entra em jogo. Você fará isso em três etapas:
- Crieum contexto. (Você pode chamá-lo de
LevelContext, pois é para o nível do título.) - Useesse contexto a partir do componente que precisa dos dados. (
HeadingusaráLevelContext.) - Forneçaesse contexto a partir do componente que especifica os dados. (
SectionforneceráLevelContext.)
O contexto permite que um pai—mesmo um distante!—forneça alguns dados para toda a árvore dentro dele.
Usando contexto em filhos próximos


Usando contexto em filhos distantes


Etapa 1: Criar o contexto
Primeiro, você precisa criar o contexto. Você precisaráexportá-lo de um arquivopara que seus componentes possam usá-lo:
O único argumento paracreateContexté o valorpadrão. Aqui,1refere-se ao maior nível de título, mas você pode passar qualquer tipo de valor (até mesmo um objeto). Você verá a importância do valor padrão no próximo passo.
Passo 2: Use o contexto
Importe o HookuseContextdo React e seu contexto:
Atualmente, o componenteHeading lê o leveldas props:
Em vez disso, remova a proplevele leia o valor do contexto que você acabou de importar,LevelContext:
useContexté um Hook. Assim comouseState e useReducer, você só pode chamar um Hook imediatamente dentro de um componente React (não dentro de loops ou condições).useContextinforma ao React que o componenteHeadingdeseja ler oLevelContext.
Agora que o componenteHeadingnão tem uma proplevel, você não precisa mais passar a prop level paraHeadingno seu JSX assim:
Atualize o JSX para que seja oSectionque o receba:
Como lembrete, esta é a marcação que você estava tentando fazer funcionar:
Observe que este exemplo ainda não funciona corretamente! Todos os títulos têm o mesmo tamanho porqueembora você estejausandoo contexto, você ainda não oforneceu.O React não sabe de onde obtê-lo!
Se você não fornecer o contexto, o React usará o valor padrão que você especificou na etapa anterior. Neste exemplo, você especificou1como argumento paracreateContext, entãouseContext(LevelContext)retorna1, configurando todos esses títulos como<h1>. Vamos corrigir esse problema fazendo com que cadaSectionforneça seu próprio contexto.
Etapa 3: Fornecer o contexto
O componenteSectionatualmente renderiza seus filhos:
Envolva-os com um provedor de contextopara fornecer oLevelContexta eles:
Isso diz ao React: “se qualquer componente dentro deste<Section>solicitarLevelContext, dê a eles estelevel.” O componente usará o valor do<LevelContext>mais próximo na árvore de UI acima dele.
É o mesmo resultado do código original, mas você não precisou passar a proplevelpara cada componenteHeading! Em vez disso, ele “descobre” seu nível de título perguntando àSectionmais próxima acima:
- Você passa uma prop
levelpara o<Section>. Sectionenvolve seus filhos em<LevelContext value={level}>.Headingsolicita o valor mais próximo deLevelContextacima comuseContext(LevelContext).
Usando e fornecendo contexto a partir do mesmo componente
Atualmente, você ainda precisa especificar olevelde cada seção manualmente:
Como o contexto permite que você leia informações de um componente acima, cadaSectionpoderia ler olevel da Sectionacima e passarlevel + 1para baixo automaticamente. Veja como você poderia fazer isso:
Com essa alteração, você não precisa mais passar a proplevel nempara o<Section>nem para o<Heading>:
Agora, tanto oHeadingquanto aSectionleem oLevelContextpara descobrir quão "profundos" eles estão. E aSectionenvolve seus filhos noLevelContextpara especificar que qualquer coisa dentro dela está em um nível "mais profundo".
Observação
Este exemplo usa níveis de título porque eles mostram visualmente como componentes aninhados podem substituir o contexto. Mas o contexto também é útil para muitos outros casos de uso. Você pode passar qualquer informação necessária para toda a subárvore: o tema de cor atual, o usuário atualmente logado e assim por diante.
O contexto passa por componentes intermediários
Você pode inserir quantos componentes quiser entre o componente que fornece o contexto e aquele que o usa. Isso inclui tanto componentes integrados como<div>quanto componentes que você mesmo pode criar.
Neste exemplo, o mesmo componentePost(com uma borda tracejada) é renderizado em dois níveis de aninhamento diferentes. Observe que o<Heading>dentro dele obtém seu nível automaticamente da<Section>mais próxima:
Você não precisou fazer nada especial para que isso funcionasse. UmaSectionespecifica o contexto para a árvore dentro dela, então você pode inserir um<Heading>em qualquer lugar, e ele terá o tamanho correto. Experimente na caixa de areia acima!
O contexto permite que você escreva componentes que “se adaptam ao seu entorno” e se exibam de forma diferente dependendo deonde(ou, em outras palavras,em qual contexto) eles estão sendo renderizados.
A forma como o contexto funciona pode lembrá-lo daherança de propriedades CSS.Em CSS, você pode especificarcolor: bluepara um<div>, e qualquer nó DOM dentro dele, não importa quão profundo, herdará essa cor, a menos que algum outro nó DOM no meio a substitua comcolor: green. Da mesma forma, no React, a única maneira de substituir algum contexto vindo de cima é envolver os filhos em um provedor de contexto com um valor diferente.
Em CSS, propriedades diferentes comocolor e background-colornão se substituem. Você pode definir a<div>’scolorcomo vermelho sem afetar obackground-color. Da mesma forma,contextos diferentes do React não se substituem.Cada contexto que você cria comcreateContext()é completamente separado dos outros e vincula componentes que usam e fornecemaquele contexto específico. Um componente pode usar ou fornecer muitos contextos diferentes sem problemas.
Antes de usar o contexto
O contexto é muito tentador de usar! No entanto, isso também significa que é muito fácil usá-lo em excesso.Só porque você precisa passar algumas props vários níveis abaixo não significa que você deva colocar essa informação em um contexto.
Aqui estão algumas alternativas que você deve considerar antes de usar o contexto:
- Comecepassando props.Se seus componentes não são triviais, não é incomum passar uma dúzia de props por uma dúzia de componentes. Pode parecer uma tarefa árdua, mas isso deixa muito claro quais componentes usam quais dados! A pessoa que mantém seu código ficará feliz por você ter tornado o fluxo de dados explícito com props.
- Extraia componentes epasse JSX como childrenpara eles.Se você passar alguns dados por muitas camadas de componentes intermediários que não usam esses dados (e apenas os repassam adiante), isso geralmente significa que você esqueceu de extrair alguns componentes ao longo do caminho. Por exemplo, talvez você passe props de dados como
postspara componentes visuais que não os usam diretamente, como<Layout posts={posts} />. Em vez disso, faça com queLayoutrecebachildrencomo uma prop e renderize<Layout><Posts posts={posts} /></Layout>. Isso reduz o número de camadas entre o componente que especifica os dados e aquele que precisa deles.
Se nenhuma dessas abordagens funcionar bem para você, considere o contexto.
Casos de uso para o contexto
- Temas:Se o seu aplicativo permite que o usuário altere sua aparência (por exemplo, modo escuro), você pode colocar um provedor de contexto no topo do seu aplicativo e usar esse contexto em componentes que precisam ajustar sua aparência visual.
- Conta atual:Muitos componentes podem precisar saber qual é o usuário atualmente logado. Colocá-lo no contexto torna conveniente lê-lo em qualquer lugar da árvore. Alguns aplicativos também permitem que você opere várias contas ao mesmo tempo (por exemplo, para deixar um comentário como um usuário diferente). Nesses casos, pode ser conveniente envolver uma parte da interface do usuário em um provedor aninhado com um valor de conta atual diferente.
- Roteamento:A maioria das soluções de roteamento usa contexto internamente para manter a rota atual. É assim que cada link "sabe" se está ativo ou não. Se você construir seu próprio roteador, talvez queira fazer o mesmo.
- Gerenciamento de estado:À medida que seu aplicativo cresce, você pode acabar com muito estado próximo ao topo do seu aplicativo. Muitos componentes distantes abaixo podem querer alterá-lo. É comumusar um redutor junto com o contextopara gerenciar estado complexo e passá-lo para componentes distantes sem muito trabalho.
O contexto não se limita a valores estáticos. Se você passar um valor diferente na próxima renderização, o React atualizará todos os componentes que o leem abaixo! É por isso que o contexto é frequentemente usado em combinação com o estado.
Em geral, se alguma informação é necessária por componentes distantes em diferentes partes da árvore, é um bom indicativo de que o contexto irá ajudá-lo.
Recapitulação
- O contexto permite que um componente forneça algumas informações para toda a árvore abaixo dele.
- Para passar contexto:
- Crie e exporte-o com
export const MyContext = createContext(defaultValue). - Passe-o para o Hook
useContext(MyContext)para lê-lo em qualquer componente filho, não importa quão profundo. - Envolva os filhos em
<MyContext value={...}>para fornecê-lo a partir de um pai.
- Crie e exporte-o com
- O contexto passa por qualquer componente no meio.
- O contexto permite que você escreva componentes que "se adaptam ao seu entorno".
- Antes de usar o contexto, tente passar props ou passar JSX como
children.
Experimente alguns desafios
Challenge 1 of 1:Substitua a prop drilling por contexto #
Neste exemplo, alternar a caixa de seleção altera a prop imageSize passada para cada <PlaceImage>. O estado da caixa de seleção é mantido no componente de nível superior App, mas cada <PlaceImage> precisa estar ciente disso.
Atualmente, App passa imageSize para List, que a passa para cada Place, que a passa para o PlaceImage. Remova a prop imageSize e, em vez disso, passe-a do componente App diretamente para PlaceImage.
Você pode declarar o contexto em Context.js.
