How to fetch data from more than one API in one project. Covid Map project - day 3.
Magda Rosłaniec
Posted on May 1, 2021
Last time I was writing about fetching the data from one API using a custom useFetch hook.
Part 2 Covid Map React project day 2
It was a few days ago. In the meanwhile, I decided to add a few more APIs and was trying to find out the best way of doing it. I felt a bit helpless because my app was crashing over and over again. In fact, it was not a problem with fetching the data but with displaying it. But this problem postponed my writing here.
Things I've done:
- After all these trials and errors I decided to still be using useFetch hook but fetch data using Promise.all().
- First, in App.js I created a list of URLs
const urls = [
'https://disease.sh/v3/covid-19/countries',
'https://disease.sh/v3/covid-19/all',
'https://disease.sh/v3/covid-19/historical?lastdays=30',
'https://disease.sh/v3/covid-19/vaccine/coverage/countries?lastdays=30'
]
and pass the urls
into the useFetch() function in useFetch.js file
const useFetch = (urls) =>
- Then I created a bunch of variables and functions using useState.
const [countries, setCountries] = useState(null);
const [countrJson, setCountrJson] = useState(null);
const [global, setGlobal] = useState(null);
const [dataHistorical, setDataHistorical] = useState(null)
const [dataVaccine, setDataVaccine] = useState(null)
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
- Next, I change a bit the
try
part of my useEffect().
const res = await Promise.all(links.map((url) => fetch(url)))
const data = await Promise.all(res.map((r) => r.json()))
Promise.all() is the JavaScript method that goes over iterable (list of links in my case) and returns a single Promise for each link or if something goes wrong we have a message about an error.
- My next step was to transform data from countries API into geoJson to display data on a map. I also need the same data as a simple json, so I created one more variable for it.
- I set all the data as React states
setCountries(geoJson)
setCountrJson(data[0])
setGlobal(data[1])
setDataHistorical(data[2])
setDataVaccine(data[3])
setLoading(false)
- I returned all the data
return { countries, countrJson, global, dataHistorical, dataVaccine, loading, error}
- To make it work I also had to access those variables in App.js component.
const { countries, countrJson, global, dataHistorical, dataVaccine, loading, error } = useFetch(urls)
useFetch.js
import { useState, useEffect } from 'react';
const useFetch = (urls) => {
const [countries, setCountries] = useState(null);
const [countrJson, setCountrJson] = useState(null);
const [global, setGlobal] = useState(null);
const [dataHistorical, setDataHistorical] = useState(null)
const [dataVaccine, setDataVaccine] = useState(null)
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
const links = urls
console.log(links)
setLoading(true);
try {
const res = await Promise.all(links.map((url) => fetch(url)))
const data = await Promise.all(res.map((r) => r.json()))
const geoJson = {
type: "FeatureCollection",
features: data[0].map((country = {}) => {
const { countryInfo = {}} = country;
const { lat, long: lng} = countryInfo;
return {
type: "Feature",
properties: {
...country,
},
geometry: {
type: "Point",
coordinates: [lat, lng]
}
}
})
}
setCountries(geoJson)
setCountrJson(data[0])
setGlobal(data[1])
setDataHistorical(data[2])
setDataVaccine(data[3])
setLoading(false)
} catch (error) {
console.log(`Failed to fetch data: ${error.message}`, error)
setError(error)
}
}
fetchData()
},[])
return { countries, countrJson, global, dataHistorical, dataVaccine, loading, error}
}
export default useFetch
And App.js
import './App.css';
import Map from './components/Map'
import Header from './components/Header'
import TableSection from './components/TableSection'
import { StyledMain } from './components/modules/Sections'
import useFetch from './useFetch'
function App() {
const urls = [
'https://disease.sh/v3/covid-19/countries',
'https://disease.sh/v3/covid-19/all',
'https://disease.sh/v3/covid-19/historical?lastdays=30',
'https://disease.sh/v3/covid-19/vaccine/coverage/countries?lastdays=30'
]
const { countries, countrJson, global, dataHistorical, dataVaccine, loading, error } = useFetch(urls)
if (error) return <p>Error!</p>;
return (
<div className="App">
<Header />
{loading ? <p>Loading ...</p> : <Map countries={countries} /> }
<StyledMain>
{loading ? "" : <TableSection countries={countrJson} /> }
</StyledMain>
</div>
);
}
export default App;
As you can see, I don't use all the data from each API yet, but I wanted to have it solved somehow before going further.
- I also refactored part of the code in Map.js to make the variables more readable.
{props.countries ? props.countries.features.map(place => {
const { coordinates } = place.geometry
const { flag, _id } = place.properties.countryInfo
const { country, cases, deaths, recovered, todayCases, todayDeaths, todayRecovered, updated } = place.properties;
let date = new Date(updated)
return (
<Marker icon={redIcon} position={coordinates} key={place.properties.country}>
<Popup >
<img src={flag} style={{width: "30px", height:"auto"}} />
<h2>{country}</h2>
<p><strong>Cases:</strong> {cases} | <strong>Cases Today:</strong> {todayCases}</p>
<p><strong>Deaths:</strong> {deaths} | <strong>Death Today:</strong> {todayDeaths}</p>
<p><strong>Recovered:</strong> {recovered} | <strong>Recovered Today:</strong> {todayRecovered}</p>
<p><strong>Last Update:</strong> {date.toLocaleDateString()}</p>
</Popup>
</Marker>
)
})
: ""}
I think I will redo this part again but for now, the popup looks like that:
Next step:
- Create a table to display data for each country.
Posted on May 1, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.