Neste artigo abordaremos um assunto bastante interessante no React chamado context, que disponibiliza uma maneira de passar os dados entre a árvore de componentes sem precisar passar props manualmente em cada nível.
Imagine o seguinte cenário, você tem o nome de um usuário em um componente, só pra gente ter uma noção entre camadas, vamos
chamá-los de A, B, C e D. Imagine que esse componente fez uma chamada de API no C, certo? Nós temos apenas o nome do usuário lá e
não queremos ter que ficar repetindo essa chamada nos outros componentes, certo?
Ah, mas por que não? Ou por que não criamos no topo e vamos chamando até embaixo?
Vamos ao primeiro cenário. Imagine que ao invés de você fazer uma requisição pela página, você faz quatro para apenas uma página,
certo? Isso é tranquilo quando se trata de um usuário, mas já pensou se a nossa aplicação tem mil acessos diários? Isso seria
basicamente quatro mil requisições diária e isso é muita coisa para o banco de dados, isso sem contar as atualizações de página.
E por que não criamos a aplicação no topo e vamos chamando nas demais? Por um motivo quase que semelhante. Não tem necessidade de
você chamar nas camadas A, B e D, se o usuário sequer passou por lá, imagine que você só entrou na primeira página e saiu, certo?
E quando você vai ver, sem querer, você fez uma requisição na sub página que se quer o usuário entrou, é bem desgastante isso, não
concorda? Ainda mais para quem vive de internet móveis.
Voltando ao Foco
Para facilitar essa comunicação, seja de A para D e D para B, por exemplo, existe o Context API do React e aí que entramos no
assunto. useContext, como funciona, de onde ele vem e como ele se alimenta. Em resumo, com o Context API podemos transitar esses
dados através de componentes sem que ele sejam filhos diretos desse componente.
E vamos com calma entendendo passo a passo dessa situação, porque até para mim foi muito complexo entender no começo.
Primeiro Passo
Primeiro, vamos importar e executar o método createContext
import { createContext } from 'react'
const MyContext = createContext(null)
Inicialmente definimos ele como null, já que queremos ele vazio e não queremos definir um tipo para ele ainda.
DICA: O retorno do createContext é um componente, por isso é legal salvar numa variável em PascalCase, as famosas { }
Segundo Passo
Já que criamos ele vazio, como iremos adicionar dados a esse novo contexto que criamos?
Dentro do Provider definimos que esse contexto fornecerá pros componentes descendentes, deixando isso na propriedade value.
const MyContext = createContext(null)
export default function Parent() {
const nameUser = {
name: 'Yagasaki',
}
const nameUser = ['Yagasaki']
const nameUser = 'Yagasaki'
return (
<MyContext.Provider value={nameUser}>
<ComponentA />
<MyContext.Provider>
)
}
Está permitido colocar qualquer tipo de dado dentro contexto, já que ele aceita qualquer tipo, isso inclui até mesmo funções.
Terceiro Passo
Agora que sabemos como criar um contexto, como adicionar dados a ele, como faremos para usar esse contexto como a gente bem entender?
Infelizmente não iremos usar esses dados fora do famoso pai-filho, ou seja, não adianta um componente "dinossauro" tentar acessar
informações do componente "peixe" já que eles não possuem nenhum parentes, é diferente do famoso "dinossauro" para "galinha", deu
para entender a analogia? Componentes que estão fora da árvore deles não possuem acesso, simples.
Mas como vamos acessar esses dados?
Primeiramente precisamos importa uma ou duas coisas, sendo: O Hook useContext
do React e o Hook que a gente criou em outro
arquivo. Abaixo está um exemplo de como iremos usar o useContext a nosso favor.
import { useContext } from 'react'
import MyContext from './MyContext.js'
export default function ComponentC() {
const userContext = useContext(MyContext)
return (
<div>
<p>{userContext.name}</p>
</div>
)
}
O seu retorno será exatamente a mesma informação que inserimos ali em cima na propriedade value
. Assim podemos obter o dado
diretamente do provider sem NENHUM componente intermediário! Economizando um esforço que seria praticamente enorme para transitar esse dado abaixo por todos os
componentes intermédios! Simplesmente DEMAIS!
DICA: Note que obtemos o valor do nome como uma propriedade de userContext, isso porque no exemplo, criamos como um objeto na propriedade value, e esse mesmo objeto foi retornado da execução do useContext. Se for outro tipo de dado, o retorno será correspondente!
Modificando o valor
Beleza, já criamos o provider, que vem do inglês de provedor, aquele que provê algo, providencia. Conseguimos inserir algo nele e acessamos ele através de um componente distante, e agora? Agora vamos aprender a modificar esse valor, manipular essa informação. Lembra que eu mencionei acima que ele aceita QUALQUER tipo de dado, inclusive funções? Então, e se por exemplo a gente usasse um useState
nele?
Você deve se perguntar porque iriamos querer fazer isso, certo? Imagine que você está numa página de edição de usuário e quer que os dados desse usuário já estejam preenchidos quando o usuário entrar na página, como que a gente faz?
A ideia é o seguinte, vamos isolar a variável que a gente está passando para a propriedade value
para facilitar a leitura e deixar um pouco mais organizado, okay? Nada de muito novo que fizemos aqui, é claro que estamos levando em consideração que ele é um objeto. Iremos apenas adicionar o valor do state e seu dispatch na variável providerValue e assim, inserimos no provider.
const MyContext = createContext(null)
export default function Parent() {
const [name, setName] = useState('Yagasaki')
const providerValue = {
name,
setName,
}
return (
<MyContext.Provider value={providerValue}>
<ComponentA />
</MyContext.Provider>
)
}
Ficou confuso? O que fizemos a mais foi apenas adicionar o nome das duas variáveis que usamos no useState
para assim que chamarmos de volta podermos manipular ele também dentro do componente, caso ao contrário, só precisariamos enviar o name
e ele ficaria somente leitura, tendo que ser alterado apenas na página Parent
Dessa forma podemos alterar o valor da variável name
a partir de qualquer componente que quisermos, como eu mencionei anteriormente, isso acaba sendo bem útil quando precisamos atualizar o valor de uma variável em ambos os lados, bacana né?
Olha como ficou o componente filho quando utilizamos o useState
para passar a informação:
import { useContext } from 'react'
import MyContext from './MyContext.js'
const ComponentA = () => {
const userContext = useContext(MyContext)
function handleChange(event) {
const { newName } = event.target.value;
if (true) {
}
userContext.setName(newName)
}
return (
<div>
<label>
Usuário: {userContext.name}
<input type="text" value={userContext.name} onChange={handleChange}/>
</label>
</div>
)
}
export default ComponentA
Só tome cuidado com loops infinitos, em casos de formulário, use o form onSubmit={(e) => e.preventDefault()}
, assim, evitará de atualizar a página quando
o usuário clicar em enviar, por exemplo, e claro, caso esteja usando um form.
Fico muito agradecido que você tenha chegado até aqui, levou um pouco de tempo para construir esse artigo, já que tentei deixar o mais direto e bem explicado possível para você entender de fato como funciona o Contexto, já que eu levei muito tempo para realmente entender como ele funciona.
Feedback no meu Twitter/X (@Yagasaki7K) são sempre bem vindos e espero que você que se dedicou a ler e entender esse artigo tenha aprendido algo novo.
Muito obrigado!