Existe todo um ecossistema React que aborda outros aspectos. Neste tutorial, você aprenderá sobre um dos elementos mais básicos de qualquer aplicativo da Web – como buscar dados para exibição. Isso não é trivial. Existem vários lugares na hierarquia do componente React onde você pode buscar dados. Quando buscar dados é outra preocupação. Você também precisa considerar qual tecnologia usar para buscar seus dados e onde armazená-los.
No final deste tutorial, você terá uma visão clara de como a busca de dados funciona no React, os prós e contras de diferentes abordagens e como aplicar esse conhecimento aos seus aplicativos React.
Começando
Vamos criar um esqueleto para nosso aplicativo React com create-react-app:
1 |
create-react-app react-data-fetcher |
O resultado é uma estrutura de diretório bastante elaborada. Leia o excelente arquivo README se você não estiver familiarizado com o create-react-app.
Criando um servidor simples
Criei um servidor simples para armazenar e servir cotações. Não é o foco deste tutorial e sua função é fornecer uma API remota para demonstrar como buscar dados com o React. Apenas para satisfazer sua curiosidade, é um aplicativo Python 3 baseado no framework hug e usa Redis como armazenamento persistente.
A API é extremamente simples. Há um único ponto final, /quotes
. Ele retorna todas as citações armazenadas em resposta a uma solicitação HTTP GET e você pode adicionar novas citações enviando uma solicitação HTTP POST.
O código-fonte completo está disponível no GitHub.
Visão geral do aplicativo de demonstração
O aplicativo de demonstração é um aplicativo React que se comunica com o serviço de cotações, exibe todas as cotações e permite adicionar novas cotações.
A estrutura do aplicativo é muito simples. Comecei com um esqueleto criado por create-react-app e adicionei dois componentes no subdiretório src: QuoteList e AddQuoteForm. Aqui está a estrutura do diretório (excluindo node_modules):
1 |
~/git/react-data-fetcher > tree -I node_modules -L 2 |
2 |
. |
3 |
├── README.md |
4 |
├── README2.md |
5 |
├── package.json |
6 |
├── public |
7 |
│ ├── favicon.ico |
8 |
│ ├── index.html |
9 |
│ └── manifest.json |
10 |
├── src |
11 |
│ ├── AddQuoteForm.css |
12 |
│ ├── AddQuoteForm.js |
13 |
│ ├── App.css |
14 |
│ ├── App.js |
15 |
│ ├── App.test.js |
16 |
│ ├── QuoteList.js |
17 |
│ ├── index.css |
18 |
│ ├── index.js |
19 |
│ └── registerServiceWorker.js |
20 |
└── yarn.lock |
21 |
2 directories, 16 files |
O código-fonte completo está disponível no GitLab.
Exibindo Cotações
O QuoteList
componente funcional exibe uma lista de citações como uma lista de marcadores. Ele espera um array de strings:
1 |
import React from 'react' |
2 |
const QuoteList = ({quotes}) => |
3 |
quotes.map(quote => <li key={quote}>{quote}</li>) |
4 |
export default QuoteList |
É um componente filho do principal App
componente.
Buscando dados com a API Fetch
A Fetch API é uma API baseada em promessa que retorna um objeto de resposta. Para obter o conteúdo JSON real, você precisa invocar o json()
método do objeto de resposta.
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
fetch(QUOTE_SERVICE_URL) |
4 |
.then(response => response.json()) |
5 |
.then(result => this.setState({quotes: result, |
6 |
isFetching: false})) |
7 |
.catch(e => console.log(e)); |
8 |
}
|
9 |
}
|
Colocando seu código de busca de dados
React é, claro, tudo sobre componentes. A questão de onde colocar o código de busca de dados é importante. Se você fatorar bem seu código, terá muitos componentes genéricos e alguns componentes específicos do aplicativo. React e JavaScript em geral são muito flexíveis, então é possível injetar lógica em qualquer lugar.
A busca de cotações de uma API REST requer alguma forma de pesquisa, pois desejo que as cotações estejam sempre atualizadas. Mas a busca inicial também é importante. Os componentes do React têm métodos de ciclo de vida onde você pode implementar a lógica que será executada em um determinado momento. O componentDidMount()
O método é acionado quando o componente pode ser acessado e seu estado modificado. É o local perfeito para iniciar a busca de dados.
Aqui está o que parece:
1 |
componentDidMount() { |
2 |
this.fetchQuotes() |
3 |
}
|
Se você realmente deseja reduzir o tempo para a primeira visualização, considere usar o componentWillMount()
para iniciar sua busca assíncrona, mas corre o risco de ter a busca concluída antes que o componente seja montado. Eu não recomendo esta abordagem.
Confira Mastering the React Lifecycle Methods para mais detalhes.
Escolhendo com que frequência buscar dados
A busca inicial em componentDidMount()
é ótimo, mas quero atualizar as cotações com frequência. Em uma API baseada em REST, a única solução é pesquisar periodicamente o servidor. O serviço de cotações é bem básico e sempre retorna todas as cotações.
Serviços mais escaláveis fornecerão uma maneira de verificar atualizações ou até mesmo usar HTTP if-modify-since ou eTag. Nosso aplicativo de demonstração apenas busca tudo a cada cinco segundos iniciando um cronômetro em componentDidMount()
e limpeza em componentWillUnmount()
:
1 |
componentDidMount() { |
2 |
this.fetchQuotes() |
3 |
this.timer = setInterval(() => this.fetchQuotes(), 5000); |
4 |
}
|
5 |
|
6 |
componentWillUnmount() { |
7 |
this.timer = null; |
8 |
}
|
A duração da pesquisa é uma decisão específica do aplicativo. Se você precisar de atualizações em tempo real e/ou a pesquisa estiver sobrecarregando muito o back-end, considere o uso de WebSockets em vez de REST.
Lidando com a busca de dados de longa duração
Às vezes, a busca de dados pode demorar muito. Nesse caso, exibir uma barra de progresso ou uma animação brilhante para que o usuário saiba o que está acontecendo pode contribuir muito para a experiência do usuário. Isso é especialmente importante quando o usuário inicia a busca de dados (por exemplo, clicando em um botão de pesquisa).
No aplicativo de demonstração, simplesmente mostro uma mensagem dizendo “Buscando cotações…” enquanto uma busca está em andamento. No render()
método do principal App
componente, utilizo a renderização condicional verificando o state.isFetching
membro.
1 |
render() { |
2 |
const title = 'Quotes for ya!' |
3 |
let now = new Date() |
4 |
return ( |
5 |
<div className='App'> |
6 |
<h2 className='App-title'>{title}</h2> |
7 |
<p>{this.state.isFetching ? 'Fetching quotes...' : ''}</p> |
8 |
<QuoteList quotes={this.state.quotes} /> |
9 |
<AddQuoteForm quote_service_url={QUOTE_SERVICE_URL}/> |
10 |
</div> |
11 |
);
|
12 |
}
|
O fetchQuotes()
método cuida da atualização state.isFetching
inicializando-o como true ao iniciar e definindo-o novamente como false ao receber as aspas:
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
fetch(QUOTE_SERVICE_URL) |
4 |
.then(response => response.json()) |
5 |
.then(result => this.setState({quotes: result, |
6 |
isFetching: false})) |
7 |
.catch(e => console.log(e)); |
8 |
}
|
9 |
}
|
Tratamento de Erros
Eu faço o mínimo de manipulação de erros aqui, registrando os erros capturados no console. Dependendo do seu aplicativo, você pode invocar alguma lógica de repetição, notificar o usuário ou exibir algum conteúdo alternativo.
Usando Axios
Uma alternativa à API Fetch é o Axios. Muitos desenvolvedores optam por substituir o Fetch pelo Axios devido ao poder extra e à conveniência da biblioteca Axios.
Aqui está o que fetchQuotes
função ficaria com Axios:
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
axios.get(QUOTE_SERVICE_URL) |
4 |
.then(response => this.setState({quotes: response.data, |
5 |
isFetching: false})) |
6 |
.catch(e => console.log(e); |
7 |
}
|
Isso é muito semelhante, mas um pouco mais conciso. A lógica de envio pode ser tão simples quanto vista abaixo.
1 |
handleSubmit = event => { |
2 |
axios.post(this.props.quote_service_url, |
3 |
{'quote': this.state.quote}) |
4 |
.then(r => console.log(r)) |
5 |
.catch(e => console.log(e)); |
6 |
event.preventDefault(); |
7 |
}
|
Com esta introdução básica ao Axios, vamos aprender mais sobre as diferenças entre as duas bibliotecas.
Diferenças entre Axios e Fetch
Transformação de Dados JSON
Se você olhar para o Axios, a sintaxe aparece como abaixo. Axios se encarrega de converter os dados para JSON
.
1 |
const url = '' |
2 |
const data = {}; |
3 |
axios
|
4 |
.post(url, data, { |
5 |
headers: { |
6 |
Accept: "application/json", |
7 |
"Content-Type": "application/json;charset=UTF-8", |
8 |
},
|
9 |
})
|
10 |
.then(({data}) => { |
11 |
console.log(data); |
12 |
});
|
Por outro lado, a sintaxe básica de fetch
é como abaixo. Com fetch
você precisa converter os dados em um string
usando JSON.stringify
.
1 |
const url = ""; |
2 |
const options = { |
3 |
method: "POST", |
4 |
headers: { |
5 |
Accept: "application/json", |
6 |
"Content-Type": "application/json;charset=UTF-8", |
7 |
}, |
8 |
body: JSON.stringify({}), |
9 |
}; |
10 |
fetch(url, options) |
11 |
.then((response) => response.json()) |
12 |
.then((data) => { |
13 |
console.log(data); |
14 |
}); |
Além disso, a resposta deve ser convertida de volta para JSON com fetch
.
Tempo limite de resposta com Axios
No exemplo acima, tivemos que implementar a lógica pela qual o endpoint da API é chamado após um tempo limite predefinido. Esta é uma das principais razões pelas quais fetch
é evitada em aplicações de tempo real. O Axios torna o tempo limite de resposta extremamente simples. Esta é uma propriedade opcional no objeto de configuração do Axios.
1 |
axios({ |
2 |
method: 'post', |
3 |
url: '', |
4 |
timeout: 5000, |
5 |
data: {} |
6 |
})
|
7 |
.then(response => {/* handle the response */}) |
8 |
.catch(error => console.error('timeout exceeded')) |
Interceptores
Uma das principais razões pelas quais os desenvolvedores preferem o Axios fetch
é o acesso a interceptadores HTTP. Por definição, os interceptadores HTTP são úteis quando você deseja alterar ou inspecionar uma solicitação HTTP. O código não precisa ser separado para cada solicitação, quando você usa interceptores. Os interceptadores HTTP fornecem uma estratégia global para lidar com solicitações e respostas.
Então, como conseguimos a interceptação HTTP com o Axios? Aqui está um trecho para ajudá-lo.
1 |
axios.interceptors.request.use(config => { |
2 |
console.log('Hello There!'); |
3 |
return config; |
4 |
});
|
5 |
|
6 |
// sent a GET request
|
7 |
axios.get('') |
8 |
.then(response => { |
9 |
console.log(response.data); |
10 |
});
|
No trecho de código acima, axios.interceptors.request.use
O método é usado para informar ao aplicativo o que deve ser feito antes de disparar a solicitação. Este seria um ótimo lugar para injetar informações de autenticação na solicitação.
Por outro lado, o código pode se tornar muito complicado quando você deseja interceptar um fetch
solicitar. Você tem que substituir a implementação global de fetch
para definir interceptores. E esta não é uma tarefa fácil ou limpa.
Solicitações Simultâneas
Em um aplicativo em tempo real, você precisará de uma solução que possa enviar várias solicitações simultaneamente. É aqui que o Axios se mostra útil. A biblioteca Axios vem com muitos métodos como all()
e spread()
para lidar com vários pedidos. Aqui está um exemplo simples, para ajudá-lo a entender como várias solicitações podem ser enviadas no axios.
Aqui o console.log
as instruções serão impressas somente depois que ambas as solicitações de API forem concluídas com êxito.
1 |
axios.todos([ |
2 |
axios.get('https://'), |
3 |
axios.get('https://') |
4 |
])
|
5 |
.then(axios.spread((a, b) => { |
6 |
console.log(a); |
7 |
console.log(b); |
8 |
}));
|
Para alcançar a lógica acima em fetch
você tem que usar Promise.all
. Vamos traduzir a lógica acima em uma fetch
solicitar.
1 |
Promessa.todos([ |
2 |
fetch('https://'), |
3 |
fetch('https://') |
4 |
])
|
5 |
.then(async([res1, res2]) => { |
6 |
const a = await res1.json(); |
7 |
const b = await res2.json(); |
8 |
console.log(a'); |
9 |
console.log(b'); |
10 |
})
|
11 |
.catch(error => { |
12 |
console.log(error); |
13 |
});
|
Conclusão
Nesta postagem, você viu muitos fatos interessantes e formas de implementação de solicitações de API em um aplicativo React.
Você aprendeu como buscar dados de forma assíncrona em um aplicativo React. E também discutimos métodos de ciclo de vida relevantes, sondagens, relatórios de progresso e tratamento de erros. Também comparamos as duas principais bibliotecas baseadas em promessas: Fetch API e Axios.
Esta postagem foi atualizada com contribuições de Divya Dev.