Coding Journal

My journey to become a Full Stack Developer

covid map react project day 2

Covid Map - React project - day 2

April 25, 2021, 9:42 p.m.

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: Covid map - React project - day 1

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
 


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
 


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]
    }
};


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)
        } 
// ...

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

 

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

At the moment the map looks like this:


 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

 

Tagged in : built-in-public

my photo

written by

Makneta

Avid learner, Python / Django and CSS Art ethusiast.

Similar posts

All posts