Entendendo a Context API do React: criando um componente de loading

William Queiroz
React Brasil
8 min readAug 10, 2018

--

cover

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 de loading para true e define uma mensagem;
  • hideLoading que muda o estado de loading para false.

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 em App.jsx e baseia-se no contexto). Também removemos do render .
  • Obtemos as funções showLoading e hideLoading 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

--

--