DEV Community

Cover image for How to fetch data from more than one API in one project. Covid Map project - day 3.
Magda Rosłaniec
Magda Rosłaniec

Posted on • Originally published at makneta.herokuapp.com

10 4

How to fetch data from more than one API in one project. Covid Map project - day 3.

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:

  1. After all these trials and errors I decided to still be using useFetch hook but fetch data using Promise.all().
  2. 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'
  ]
Enter fullscreen mode Exit fullscreen mode

and pass the urls into the useFetch() function in useFetch.js file

const useFetch = (urls) => 
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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()))
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode
  • I returned all the data
 return { countries, countrJson, global, dataHistorical, dataVaccine, loading, error}
Enter fullscreen mode Exit fullscreen mode
  • 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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.

  1. 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>
        )
      })
      : ""} 
Enter fullscreen mode Exit fullscreen mode

I think I will redo this part again but for now, the popup looks like that:
Alt Text

Next step:

  1. Create a table to display data for each country.

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)