Entendendo a Context API do React: criando um componente de loading
Fala devs! Antes de iniciarmos, gostaria de agradecer o alcance e o apoio dos leitores do meu primeiro post aqui sobre os relacionamentos no Elasticsearch! Então, você que está aqui, dá uma lida lá! HAHA
Bom, comecei a trabalhar com o React a pouco tempo e foi amor a primeira vista! ❤ A facilidade em que a biblioteca oferece para a construção de SPA’s é incrível! Porém, há certos conceitos que precisam ser bem estudados e colocados em prática pra então serem compreendidos. Se você não conhece nadinha de nada sobre o React, aconselho dar uma pausa na sua leitura agora, salvar esse post e aprender um pouco mais sobre a lib, pois a context API exige ao menos os conhecimentos básicos da biblioteca (criação de componentes reutilizáveis, manipulação de estado e props).
Você pode encontrar os detalhes de como começar com o React na documentação oficial da biblioteca aqui.
Veja o resultado do projeto aqui: https://codesandbox.io/s/8yz442ql40
Você também pode clonar o repositório do projeto aqui: https://github.com/wnqueiroz/react-context-api-loading
O que é a Context API?
Bem, quando acessamos as docs da Context API de cara vemos a seguinte mensagem:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Traduzindo a grosso modo, a Context API, fornece uma maneira de passar os dados de componentes sem ter que passar manualmente em todos os níveis. Tá, mas o que isso quer dizer? Observe a imagem:
No uso básico do React, quando precisamos que um componente filho tenha ou manipule os dados do pai, precisamos informar esses dados e funções nas props
do componente filhos. O que é bem simples na realidade, seria algo como:
Esse é um exemplo simples, que mostra somente o componente A, passando a prop
“example” para o componente B. Num componente tão simples, não conseguimos visualizar de imediato o “problema” que isso seria, quando tivermos mais componentes filhos e dependentes na nossa aplicação. Em aplicações maiores podemos ter situações diferentes desta. Imagine que temos um dado que deve ser compartilhado entre mais componentes, os dados de um usuário, quando logado na aplicação, por exemplo. Precisamos exibir o nome do usuário em vários lugares, fiz esse desenho para exemplificar melhor:
Agora, tente imaginar a seguinte hierarquia e organização dos componentes dessa tela:
Perceba que os dados do usuário são utilizados em diferentes componentes (fiz bem aninhado mesmo para terem uma ideia real do que estou falando), desta maneira deveríamos passar os dados entre os componentes, até atingirmos os interessados (settings e user-avatar), porém, quando fizermos isso, notamos que não tem porquê alguns componentes terem acesso a propriedade user
. Além do mais, para evitar passar user
para todos os componentes, deveríamos armazenar esse dado no componente de maior hierarquia (no nosso caso, app) o que gera uma manutenção trabalhosa, e logo não é uma boa prática deixar esse dado ali. Se você já tem experiência e familiaridade com o React logo deve estar pensando: “pow, mas pra isso eu posso usar o Redux e fechou!”. SIM! Mas calma! a ideia aqui é apresentar este “novo” conceito da context API.
Com a context API nesse cenário, podemos criar um contexto que armazene os dados do usuário, e a partir disso, os componentes interessados nesse dado, “consumirão” os dados do contexto! Confuso? Calma de novo! HAHA
Eu utilizei esse exemplo do usuário para dar uma visão maior sobre o uso de dados compartilhados na aplicação, mas, na própria documentação, temos um exemplo mais simples, que é um contexto criado que FORNECE os dados do tema corrente na aplicação, e os componentes interessados CONSOMEM esses dados e trabalham da maneira que acharem melhor. Atente-se nessas palavras: fornecer e consumir, são palavras chaves que lhe ajudarão melhor a entender o conceito.
Depois desse overview, vamos começar a entender melhor como funciona a context API, criando um componente de loading. Para isso, criei o seguinte cenário: Numa aplicação com React, há alguns momentos em que executamos chamadas a APIs externas para nos prover alguns dados, e nesses momentos, para melhorar a experiência do usuário (e evitar também alguma interação dele num momento inoportuno) exibimos uma mensagem ou uma tela informando o carregamento ou processamento de um determinado conteúdo. Nisso, teremos um componente Loading
para indicar o carregamento de conteúdo.
Vamos começar!
Criando a aplicação
Vamos rodar o seguinte comando abaixo para iniciarmos um projeto React com o pacote create-react-app
:
$ npx create-react-app context-api-loading
Isso irá criar uma pasta chamada context-api-loading
com o React já configurado, abra-a num editor de texto de sua preferência e faça as seguintes mudanças:
Execute o seguinte comando para adicionar o react-spinkit
como dependência:
$ yarn add react-spinkit
ou $ npm i react-spinkit
- Apague os arquivos de
src
:
- App.css
- App.test.js
- logo.svg
- Deixe o arquivo
src/App.js
como abaixo:
- Deixe o arquivo
src/index.js
como abaixo:
- Deixe o arquivo
src/index.css
como abaixo:
- Iremos simular algumas requisições a API na nossa aplicação, ao decorrer do post veremos como, mas antes, crie o arquivo
src/mock.js
com o código abaixo:
- Para simularmos as requisições, criaremos um arquivo para as nossas chamadas a API. Crie-o em
src/services/api.js
com o código abaixo:
Montamos a estrutura da nossa aplicação! Vamos agora criar os componentes Users
e Departments
, crie uma nova pasta em src
chamada components
e salve os arquivos lá:
src/components/Users.jsx
:
src/components/Departments.jsx
:
Altere o arquivo App.jsx
, importando os componentes criados:
Caso tenha seguido os passos, teremos esse resultado na aplicação:
Note que estamos simulando uma requisição a API, então, quando clicamos para buscar os dados, esses levam um certo tempo até serem enviados a nossa aplicação, é nesse cenário que criaremos o nosso “Carregando…”.
Criando o componente Loading
Vamos começar criando o componente de loading para exibir as mensagens, com o código abaixo:
src/components/Loading.jsx
:
Para vermos o funcionamento, vamos alterar os componentes Users
e Departments
, para quando iniciarmos a requisição, exiba o loading, e então, remova-o:
src/components/Users.jsx
:
src/components/Departments.jsx
:
Teremos o seguinte resultado:
Integrando com a Context API
Se notarmos bem, temos dois componentes que importam o mesmo componente de loading, o mesmo será renderizado por todos os outros componentes da aplicação, que precisam exibir uma mensagem de carregamento. Para compartilharmos melhor a funcionalidade de loading entre os componentes vamos usar a Context API.
Vamos começar criando o contexto para o Loading:
src/App.jsx
:
O primeiro parâmetro da função React.createContext
espera receber um valor padrão para o contexto, pode ser uma string
para (contextos mais simples), ou um objeto (contextos mais complexos).
Depois de criarmos, temos a disposição em LoadingContext
os components Consumer
e Provider
, vamos entender melhor:
Provider: é um componente que “fornece” os dados do contexto, bem como as suas mudanças, aos seus “consumidores” ( Consumers
do mesmo contexto).
Consumer: é um componente que “consome” os dados do contexto. Seu children
espera receber uma função (que recebe “as props” do contexto) que retorne um componente. Se um Consumer
for renderizado sem um Provider
como pai, “as props” recebidas serão as que foram informadas como padrão no React.createContext
.
Clique nos links para mais detalhes sobre Provider e Consumer.
A Context API ainda não funciona, para isso precisamos usar o Provider e o Consumer disponibilizados em LoadingContext
. Dito isso, vamos a implementação deles!
Faça as alterações em src/App.jsx
:
Analisando o código:
1 - Criamos o seguinte trecho dentro de src/App.jsx
:
Criamos o estado que será usado pelo Provider
em state
( loading
e message
). Em seguida, criamos as funções:
showLoading
que muda o estado deloading
paratrue
e define uma mensagem;hideLoading
que muda o estado deloading
parafalse
.
2 - No render
declaramos o Provider
que espera receber na propriedade value
algum valor relacionado ao contexto:
O Provider
então, atualizará o contexto com esses valores informados.
3 - Ainda no render, criamos os valores para o contexto, substituindo os valores padrões informados em React.createContext
. Queremos que os Consumers
utilizem os dados e funções do nosso componente App:
Pegamos as funções showLoading
e hideLoading
do nosso componente App
, e em seguida, “clonamos” o estado do componente App
e passamos as funções para value
(o Provider
substituirá os valores padrões do contexto). Logo, o nosso contexto está baseando-se nas funções e estados controlados por App. Guarde isso!
4 - No children
do LoadingContext.Provider
criamos o nosso Consumer
. O Consumer
espera receber uma função que retorne um elemento/componente React. Essa função, recebe os dados do contexto, logo, conseguimos obter showLoading
, hideLoading
, loading
e message
(informaremos essas propriedades pros componentes interessados):
5 - Importamos o componente Loading
no App.jsx
e retornamos na função os componentes com as propriedades do contexto:
Perceba que Users
e Departments
recebem as props showLoading
e hideLoading
e o componente Loading
somente loading
e message
. Isso porque os componentes são interessados em propriedades diferentes, dito isso, podemos estabelecer diferentes funcionalidades aos nossos componentes, baseando-se no mesmo contexto!
Essas propriedades, ficarão disponíveis em
this.props
de cada um dos componentes
Integramos a Context API (com o contexto LoadingContext
que criamos). Agora, basta alteramos os componentes Users
e Departments
para usarem as propriedades que informamos ( showLoading
e hideLoading
)
Faça as seguintes mudanças nos arquivos:
src/components/Departments.jsx
:
src/components/Users.jsx
:
Analisando as mudanças:
- Removemos a importação do componente
Loading
dos dois componentes (uma vez que esse é importado emApp.jsx
e baseia-se no contexto). Também removemos dorender
. - Obtemos as funções
showLoading
ehideLoading
das propriedades (this.props
) e utilizamos nos momentos adequados dentro do componente.
Finalizando…
Bem, é isso! Espero que tenham entendido como funciona essa bendita lib do React e que tenham boas ideias para utilizá-la!
Quero agradecer a você que chegou até aqui, e queria lhe pedir também para encaminhar-me as suas dúvidas, comentários, críticas, correções ou sugestões sobre a postagem.
Nenhum conhecimento é válido, se ele não é repassado!
EXTRA: O post ficou bem extenso (devido a quantidade de código e detalhes), MAS, em breve farei um outro post, como complemento deste, para como criar uma estrutura melhor dos seus contextos, providers e consumers, e passar as propriedades do contexto utilizando HoC’s!
Muito obrigado e até a próxima! :D