React Tutorial: Creating coronavirus tracker using React-vis and Material-UI

 

GITHUB

The idea is simple first we are going to fetch the data of all the country which are infected from here novelcovid API. When the user clicks on the country flag it should display all the information such as cases, deaths, total number of cases, etc.

We will then display a graph which will contain two information:

  1. Total number of cases and
  2.  Total number of deaths in that country.

Implementation

For our app, we will require two dependencies: Material UI for styling and another is React-vis which is a react framework created by uber for drawing graph and chart.

Install both the packages by writing the following commands:

npm install i @material-ui/core react-vis

Now let’s create our first component named App.js which will fetch the data. Open up your editor and write the following code:

App

import React from 'react';
import AllCountry from "./AllCountry";
import CssBaseline from '@material-ui/core/CssBaseline';
import Container from '@material-ui/core/Container';
import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import CircularProgress from '@material-ui/core/CircularProgress';

const URL = "https://corona.lmao.ninja/countries"

const useStyle = makeStyles((theme) => ({
    paper: {         display : 'flex',
        flexWrap: 'wrap',
        '& > *': {
            margin: theme.spacing(3),
            width: theme.spacing(700),
            height:theme.spacing(70),
        },
    },
    loading: {
        '& > *': {
            margin: theme.spacing(20),
            marginLeft: '45%',
        },
    }
}));

const App = () => {
    const [data,setData] = React.useState(null);
    const classes = useStyle();

    React.useEffect( () => {
        setTimeout(() => {
            fetch(URL).then(response => {
                if( response.ok ) { return response.json(); }
                else { throw new Error("Something Went Worng"); }
                })
                .then( data => setData(data))
                .catch(error => {console.log(error);})
            },5000);
    });

    return (
        <div >
            <CssBaseline />
            <Container maxWidth='lg' >
                <div className = {classes.paper}>
                    <Paper evaluation = {3} >
                        {
                            (data !== null) ?
                                <AllCountry data={data} /> :
                            <div className = {classes.loading} >
                                <CircularProgress size = {100}/>
                            </div>
                        }
                    </Paper>
                </div>
            </Container>
        </div>

    )
}

export default App;

In the above code, we have used here React.useEffect which is the new feature of react called React hook which is called whenever our component gets mounted. In this method, we fetch our data from the site.

Then in the App function we have first defined our state using React.useState method initially which is null. The state which in our case is the data gets updated every 5 seconds.

Then in return part, we simply check whether the data is null or not that is whether the data is fetched from the website or not. If not it will show us a circular progress bar or if the data is fetched this data is passed to AllCountry component as props.

AllCountry

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import GridList from '@material-ui/core/GridList';
import GridListTile from '@material-ui/core/GridListTile';
import GridListTileBar from '@material-ui/core/GridListTileBar';
import Typography from '@material-ui/core/Typography'
import Display from './Display';


const useStyle = makeStyles((theme) => ({
    gridlayout: {
        display : 'flex',
        flexWrap: 'wrap',
        margin: theme.spacing(1),
        justifyContent: 'space-around',
        overflow: 'hidden',
    },
    gridList: {
        width : 700,
        height : 450,

    },
    heading: {
        margin: theme.spacing(1),
    },
}));


function handleFlagClick(index,data,setData) {
    const url = "https://corona.lmao.ninja/v2/historical/" + data[index].country
    fetch(url).then(response => {
        if( response.ok ) { return response.json() }
        else { throw new Error("Something Went Worng"); }
        })
        .then( d => setData({
            "overall": data,
            "historic": d,
            "index" : index}))
        .catch(error => {console.log(error);})

}

const AllCountry = (props) => {
    const classes = useStyle();
    const [countryData,setData] = React.useState(null);
    return(
        <div className>
        {
            (data === null) ?
                <div className = {classes.gridlayout}>
                    <Typography className = {classes.heading}
                    variant = "h4"
                    color = "primary">
                        All Country
                    </Typography>
                    <GridList  className = {classes.gridList} cols = {4} spacing = {5}>
                        {
                            props.data.map((d,index) => (
                                <GridListTile key = {d.country.flag} >
                                    <img className = {classes.button}
                                    id = {index}
                                    onClick = {(e)=>{handleFlagClick(e.target.id,props.data,setData);}}
                                    src = {d.countryInfo.flag}  />
                                    <GridListTileBar
                                        title={d.country}
                                        subtitle={<span>{d.countryInfo.iso3}</span>}
                                    />
                                </GridListTile>
                                ))
                        }
                    </GridList>
                </div> :
                <div>
                    <Display index = {countryData.index} overall = {countryData.overall} history = {countryData.historic} />
                </div>

            }
            </div>
    );

}

export default AllCountry;

In this component we display all the country flags along with the country name  in a Grid from. Whenever a user clicks on the certain flag it triggers handleFlagClick function. This function takes the index of the flag which was clicked and the name of that country. After taking name it fetches the historic data of that country and updates the state which in our case is countryData.

After getting the country name and its historic data, this data is passed through Display component where it gets displayed.

AllCountry.js

 

Display


import React from 'react';
import '../../node_modules/react-vis/dist/style.css';
import DiscreteColorLegend from '../../node_modules/react-vis/dist/legends/discrete-color-legend';
import {XYPlot,LineSeries} from 'react-vis';
import {HorizontalGridLines,VerticalGridLines} from 'react-vis';
import {XAxis,YAxis} from 'react-vis';
import { makeStyles } from '@material-ui/core/styles';
import Box from '@material-ui/core/Box'
import Divider from '@material-ui/core/Divider';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import AllCountry from './AllCountry';

function getPlottingData(history)
{
    const data = [];
    var i = 0;
    for(var  date in history )
    {
        data[i] = { x : Date.parse(date), y : history[date] }
        i++;
    }
    return data
}

const useStyle = makeStyles((theme) => ({
    content: {
        margin: theme.spacing(7),
    },
    graph: {
        marginLeft: theme.spacing(15),
        marginTop: theme.spacing(3),
    }
}));

const Display = (props) => {
    const classes = useStyle();
    const [buttonClicked,setState] = React.useState(false)

     var items =  [
        {title: 'Deaths', color: 'red'},
        {title: 'Cases', color: 'blue'},
      ]


    return(
        <div >
            {
             (buttonClicked === false) ?
            <Box display = "flex" flexDirection = "row">
                <Box className = {classes.content} display = "flex" flexDirection = "column" >
                    <Typography color = 'primary' variant = 'h3'>
                        {props.overall[props.index].country}
                    </Typography>
                    <br />
                    <Typography color  = "textSecondary" variant = "body1" >
                        Total Cases: {props.overall[props.index].cases}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "primary" variant = "body1" >
                        Today Cases: {props.overall[props.index].todayCases}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "secondary" variant = "body1" >
                         Total Death: {props.overall[props.index].deaths}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "secondary" variant = "body1" >
                        Today Death: {props.overall[props.index].todayDeaths}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "success" variant = "body1" >
                        Recovered:  {props.overall[props.index].recovered}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "textSPrimary" variant = "body1" >
                        Active:  {props.overall[props.index].active}
                    </Typography>
                    <Divider variant = "middel" />

                    <Typography color  = "error" variant = "body1" > Critical:  {props.overall[props.index].critical}
                    </Typography>
                    <Divider variant = "middel" />
                    <br />
                    <br />
                    <Button onClick  = {() => { setState(true) } } color = 'primary' variant = 'contained' >
                        Back
                    </Button>
                </Box>

                <Box display = "flex" className = {classes.graph} >
                    <XYPlot xType="time" height = {500} width = {600} >
                        <XAxis tickLabelAngle={-45} />
                        <YAxis tickLabelAngle={-90} />
                        <VerticalGridLines />
                        <HorizontalGridLines />
                        <LineSeries
                        data={getPlottingData(props.history.timeline.deaths)}
                        strokeWidth = {5}
                        color = 'red'
                        />
                        <LineSeries
                        data={getPlottingData(props.history.timeline.cases)}
                        strokeWidth = {5}
                        />
                    </XYPlot>
                    <DiscreteColorLegend
                      height={100}
                      width={100}
                      items={items}
                    />
                </Box>
            </Box> :
            <AllCountry data = {props.overall} />
            }
        </div>



    )
}

export default Display;

The Display component displays the corona data. It uses the material’s Box component to make for layout which makes the graph on the right side and remaining information on the left side of the screen. For the graph, we have used react-vis framework. The way it draws graph is first it takes height and widht of the graph using XYPlot component. We then make the grid in our graph by using HorizontalGridLines and VerticalGridLines which draws horizontal and vertical lines respectively. We have used here LineSeries component which draws lines in the graph. The data is passed as props which consist of x-axis points and y-axis points. The DiscreteColorLegend component displays the legend.

 

Display.js
Display.js

Sharing is caring!

Leave a Comment

Your email address will not be published. Required fields are marked *

shares
Scroll to Top