DEV Community

Cover image for Covid map - React project - day 2
Magda Rosłaniec
Magda Rosłaniec

Posted on • Updated on • Originally published at makneta.herokuapp.com

Covid map - React project - day 2

Yesterday I started a project using React, Leaflet.js and Open Disease Data API. In this series, I'm writing about things I'm doing and problems I'm encountering while creating this project.
Part 1: https://dev.to/makneta/covid-map-react-project-day-1-29fd

Things I've done on day 2:

  • Fetched data from disease.sh about each country
  • Formatted data into GeoJSON
  • Displayed a marker for each country with a popup that contains basic data

Problems I've encountered and my solutions:

1. I wanted to use another approach for storing data from an API and decided to create a custom useFetch hook.

While building a custom hook we are extracting component logic into a reusable function. So a custom hook is placed in a separate file in the src folder and it needs to start with use and it also has the ability to call other hooks.

In the useFetch function, I'm passing URL as a parameter and I'm using useState and useEffect with Fetch API. Almost the same as if I was fetching the data inside App.js or any component.

The function returns 3 elements: data from API, loading and error.

//useFetch.js

import { useState, useEffect } from 'react';

const useFetch = (url) => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
       const fetchData = async () => {
        setLoading(true);
        try {            
            const res = await fetch(url);
            const json = await res.json();
            setData(json)          
            setLoading(false)
        } catch (error) {
          console.log(`Failed to fetch countries: ${error.message}`, error)
            setError(error)
        }
       };
       fetchData()
    }, [url])
    return { data, loading, error}
}

export default useFetch
Enter fullscreen mode Exit fullscreen mode

The next step is to access the data, loading and error in the Map.js component.

//Map.js

import useFetch from '../useFetch';

const Mapt = () => {
  const url = 'https://disease.sh/v3/covid-19/countries'
    const { data, loading, error } = useFetch(url)
    console.log(data)    

    if (error) return <p>Error!</p>;
    if (loading) return <p>Loading...</p>;
  return (
      <MapContainer></MapContainer>
      )
}
export default Map
Enter fullscreen mode Exit fullscreen mode

At the moment I'm not using any data yet.

2. To display the data on a map I needed to format them into GeoJSON.

What is GeoJSON?

From Wikipedia:

GeoJSON is an open standard format designed for representing simple geographical features, along with their non-spatial attributes. It is based on the JSON format.

On Leaflet.js we can find an example code of GeoJSON

// from Leaflet.js
var geojsonFeature = {
    "type": "Feature",
    "properties": {
        "name": "Coors Field",
        "amenity": "Baseball Stadium",
        "popupContent": "This is where the Rockies play!"
    },
    "geometry": {
        "type": "Point",
        "coordinates": [-104.99404, 39.75621]
    }
};
Enter fullscreen mode Exit fullscreen mode

Now I need to do the same with my own data. At first, I was trying to create this GeoJSON in my Map.js file. But I was wrong. It needs to be done in the useFetch hook, just after fetching the response from API.

So I'm creating a geoJson object with the type "FeatureCollection". Because API contains hundreds of arrays I need to loop through all of them using map() to have access to those features.

// useFetch.js
// ... 
try {            
      const res = await fetch(url);
      const json = await res.json();

      const geoJson = {
          type: "FeatureCollection",
          features: json.map((country = {}) => {
          const { countryInfo = {}} = country;
          const { lat, long: lng} = countryInfo;
          return {
               type: "Feature",
               properties: {
                  ...country,
              },
               geometry: {
                  type: "Point",
                  coordinates: [lat, lng]
              }
            }
           })
        }
          setData(geoJson)
          setLoading(false)
        } 
// ...
Enter fullscreen mode Exit fullscreen mode

Thanks to it I'm having access to all data from properties as well as coordinates. Those pairs of latitude (lat) and longitude(lng) are one per country.

3. Now I can access the data in Map.js. I'm using a ternary operator to check if there are any data and if data exists it's displaying the markers and popups, otherwise it should show nothing.

const Map = () => {
// I won't be rewriting the whole code only the part in which I'm displaying the Markers
// ...

return (
<MapContainer>
 {data ? data.features.map(country => {
   return (
    <Marker icon={redIcon} position={country.geometry.coordinates} key={country.properties.country}>
     <Popup>
       <h2>{country.properties.country}</h2>
       <p>Cases: {country.properties.cases}</p>
       <p>Deaths: {country.properties.deaths}</p>
       <p>Recovered: {country.properties.recovered}</p>
       <hr />
       <p>Cases Today: {country.properties.todayCases}</p>
       <p>Death Today: {country.properties.todayDeaths}</p>
       <p>Recovered Today: {country.properties.todayRecovered}</p>
        <hr />
        <p>Last Update: {country.properties.updated}</p>
      </Popup>
     </Marker>
        )
      })
    : null}
</MapContainer>
// ... 
)
}
export default Map
Enter fullscreen mode Exit fullscreen mode

I'm aware that my Popups aren't clean. It can be done better.

At the moment the map looks like this:
Alt Text

Next step(s):

  • Refactor code in Popups part
  • Add country shapes and colours
  • Find out if I can create one useFetch for a few APIs

Top comments (0)