fbpx

In this post, I will talk about building a simple react app, for guessing the score. What score? I am glad you asked. Here in Sydney, Australia we have been in lockdown for the last 10+ weeks where we have limitations of not going anywhere beyond 5kms of where we live. We are sort of like prisoners and given our history, this is kinda ironic isn’t it? We are pretty bored here and listening to our ineffective politicians announce the daily Covid case numbers at 11am is a part of our daily routine. Over the last 10 weeks of this lockdown routine, the case numbers have become like a scorecard for a sporting event. I mean, it’s like waiting to hear the score for a game (of test cricket?) being played overseas. The daily case numbers no longer have that shock factor. My friends and I play a little guessing game where we cast our predictions for the case numbers before 11am. Yes we are all bored!!! Since I am an Software engineer I thought I should build something for fun, I mean I am not doing much else in my free time. I thought about building a “Guess the score” react app or “guess covid case numbers” react app that you can play with a group of friends.

This little app building exercise or a fun little activity and can only add to my experience with the react framework. To be perfectly honest, I still like Angular? or wait do I? I mean all these Javascript frameworks are the same anyway. You know one, you can easily pick up another one.

Covid case numbers – Requirements

The goal is to build something quick (in a day) so that it can be used ASAP. At this stage we won’t be building anything more than an online portal to track our daily case numbers predictions. We will build something that’s accessible over a browser that people can use to enter their daily predictions.

Design or layout

The (web) app will have only 1 screen with a form for the user to enter their daily prediction and a list to show other entries for the day. Given the nature of this fun project, let’s not plan too much and start building a react app with this.

Covid case numbers react app

We will start off by creating a react app and if you like you can read the instructions in my post, Building a react app for a Java backend. Once we have initialised our react app we can start thinking about building out the app’s functionality.

Components

As identified earlier, the app will have a screen with a form for entering daily predictions and a list to show the predictions. To facilitate this, we would need some sort of form wouldn’t we…

Daily prediction form

This can’t be too hard can it? I don’t think so, the only challenge would be to “wire up” the form component to the rest of the app for those unfamiliar with react. To keep things simple, instead of styling this with our own css, we will use a library like material-ui and call this component, DailyEntryForm. Here’s the code for it

import { TextField } from "material-ui";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import React, { Component } from "react";

export class DailyEntryForm extends Component {

    state = {
        username: "",
        score: "",
        date: "For date: " + this.todayDateStr
    };
    constructor() {
        
        super()
        this.todayDateStr = new Intl.DateTimeFormat("en-GB", {
            year: "numeric",
            month: "long",
            day: "numeric"
        }).format(new Date());
        this.state.date = "For: " + this.todayDateStr;
    }
    submitForm = () => {
        const { username, score, date } = this.state;
        let today = new Date().withoutTime();
        let time = today.getTime();
        let currentPredictions = JSON.parse(localStorage.getItem(time));
        if(!currentPredictions) {
            currentPredictions = {};
            currentPredictions[time] = [];
        }
        //generate a random no to 4 decimal places
        let uniqueKey = Math.round(Math.random() * 10000) / 10000;
        let id = `${username}_${uniqueKey}`;
        currentPredictions[time].push({username: username, score: score, id: id});
        localStorage.setItem(time, JSON.stringify(currentPredictions));
    }
    handleChange = input => e => {
        this.state[input] = e.target.value
        this.setState(this.state);
    }
    
    render() {
        return (
            <MuiThemeProvider>
                <React.Fragment>
                    
                    <hr />
                    <form onSubmit={this.submitForm}>
                        <TextField  id="todayDate"
                        label="Today's date" 
                        hintText="Prediction date" 
                        value={this.state.date}
                        InputProps={{ readOnly: true }}
                        variant="outlined"
                        />
                        <br />
                        <TextField id="username"
                        label="Your username"
                        hintText="Your name, can be anything"
                        floatingLabelText="Your username...can be anything"
                        onChange={this.handleChange('username')} 
                        defaultValue = {this.state.username}/>
                        <br />
                        <TextField id="prediction"
                        label="Your daily prediction"
                        hintText="Your daily predictions?"
                        floatingLabelText="Score predictions?"
                        onChange={this.handleChange('score')}
                        defaultValue = {this.state.score} />

                        <p>
                        <button  label="Add 🤞">
                            Add 🤞
                        </button>
                        </p>
                    </form>
                </React.Fragment>
            </MuiThemeProvider>
        )
    }
}

export default DailyEntryForm;

I will briefly explain the code above, if you have questions, please leave a comment. The code above is for a html form with 2 input fields and a submit button. The <MuiThemeProvider> , <TextField> classes are from material-ui and come with their own styles so it takes care of the styling for us. Most of the code above is straightforward react code, except the method to handle submit form event. There’s a little bit of logic there, which could use a bit of explaining.

To know what entries have been added…

If you look at the code to submit the form,

submitForm = () => {
        const { username, score, date } = this.state;
        let today = new Date().withoutTime();
        let time = today.getTime();
        let currentPredictions = JSON.parse(localStorage.getItem(time));
        if(!currentPredictions) {
            currentPredictions = {};
            currentPredictions[time] = [];
        }
        //generate a random no to 4 decimal places
        let uniqueKey = Math.round(Math.random() * 10000) / 10000;
        let id = `${username}_${uniqueKey}`;
        currentPredictions[time].push({username: username, score: score, id: id});
        localStorage.setItem(time, JSON.stringify(currentPredictions));
    }

To summarise it, the way this will work is for any given day we may have 0 to n entries (predictions) relative to the number of participants. We must save data in a way that we have a unique identifier for a day that maps to an array of predictions for that day. Like a key value mapping, with a unique id key for a day mapping to an array.

Let’s take a step by step walkthrough to understand it better.

  1. Get the current date without time via the without time method we added to Date.prototype, I will share that code later
  2. Check if there are any predictions for the current date in case this isn’t the first prediction of the day we are adding
  3. If there are no prediction then create a new currentPredictions array
  4. Push a new entry to our array with values obtained by destructuring the state variable
  5. Remember, localStorage only saves values as strings hence the currentPredictions array is converted to a string via json.stringify(currentPredictions) before saving

Now the code to get without time,

Date.prototype.withoutTime = function () {
  var d = new Date(this);
  d.setHours(0, 0, 0, 0);
  return d;
}

We want all predictions for a day to be stored in a single array and every time we initialise a date in Javascript, we get it down to the milliseconds elapsed. Hence it the date will be different every time we initialise it. In the context of our solution, we could be creating a new array every time a participant enters the date time value. Therefore let’s eliminate the possibility of creating multiple arrays for each day by setting the time to midnight every time we create a new date. This means, we have a unique key for each day to save our daily entries (predictions) irrespective of the time in the day when entered. Once, the daily predictions are saved, it’s time to think about displaying them

Displaying daily predictions

Here’s the code for the daily predictions component,

import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { List, ListItem } from 'material-ui/List';
import { AppBar, Divider } from 'material-ui';


Date.prototype.withoutTime = function () {
    var d = new Date(this);
    d.setHours(0, 0, 0, 0);
    return d;
}

Date.prototype.dateOnlyStr = function() {
  let dateStr = new Intl.DateTimeFormat("en-GB", {
      year: "numeric",
      month: "long",
      day: "numeric"
  }).format(new Date());
  return dateStr;
}

/**
 * Link for date format: https://www.carlrippon.com/formatting-dates-and-numbers-in-react/
 * @returns 
 */
 export class DailyPredictions extends Component {

    state = {
        dailyPredictions: [],
        date: new Date().dateOnlyStr()
    };
    constructor() {
        super();
        this.fetchList();
    }
    fetchList() {
        let today = new Date().withoutTime();
        let currentPredictions = localStorage.getItem(today.getTime());        
        if(currentPredictions) {
            let parsedObj = JSON.parse(currentPredictions);
            this.state.dailyPredictions.push(...parsedObj[today.getTime()]);
        }
    }
  
    render() {
        return (
            <MuiThemeProvider>
              <React.Fragment >
                <AppBar title="Guess the daily case numbers" />
                <h2>{this.state.date}</h2>
                <List className="Name-list" onChange={this.rerender}>
                  {this.state.dailyPredictions.map((elem) => (
                    [
                    <ListItem 
                    primaryText={elem.username}
                    secondaryText={elem.score} 
                    key={elem.id}>
                    </ListItem> ,
                    <Divider variant="inset" component="li" />
                    ]
                  ))}
                </List>
              </React.Fragment>
            </MuiThemeProvider>
          )
    }
}

  export default DailyPredictions;

The comment above the class declaration contains a link to the article that helped me while learning react. Once again we are using elements like AppBar, MuiThemeProvider and List from material-ui. Great!!! We now have the components we need for a working react app that keeps track of our daily covid case number predictions. All we need to do is add it to our app.js file and we should be able to see what we’ve built. Here’s app.js

import './App.css';
import React from 'react';
import DailyEntryForm from './components/DailyEntryForm';
import DailyPredictions from './components/DailyPredictions';

function App() {
  
  return (
    <div className="App">
      <DailyPredictions />
      <div style={{paddingTop: '20px'}}>
        <DailyEntryForm />
      </div>
    </div>
  );
}

export default App;

Like the blog? Subscribe for updates

With the app now fully working, let’s look back at why we built this? we built this to track the daily covid case number predictions so maybe it would be good to host this somewhere, right? I mean, so people can access it on their phone or browser to enter their predictions.

Hosting the react app

There are endless hosting options but for this fun project but let’s pick something easy to work with. The 2 options I can think of, are Firebase and AWS Amplify and given my past experience, I will be using Firebase here. You can sign up for a free Firebase account and use it’s features.

Firebase setup & deploy

To illustrate this, let me try another approach as opposed to describing it over a lot of text. Open the terminal, command prompt or Git bash or whatever else you like to type out the below commands

Step 1: 
cd guess-the-score

Step 2: 
npm install -g firebase-tools

Step 3: 
firebase login

// you will be taken to the firebase web page where you can login

Step 4: 
Create a new project in your firebase dashboard

Step 5:
//Back in your terminal type 
firebase init

/* After the init command, you will have to make a series of choices, the article below can shed more light into it
https://dev.to/davidkou/how-to-deploy-a-react-app-to-firebase-host-for-free-401p
*/
//After all the above, for the last step, in the terminal type 
Step 6:
firebase deploy

…and that is it! You will get the url for your deployed project in the terminal. Simple, right? Honestly when I first did this, I was quite shocked as well. I was like woah!!!! can this really be that simple?

You can get all the source code for this app here on Github.

Conclusion

We have only just touched the surface here, and number of ways in which we can continue enhancing this are endless. However for the purpose of this post, we will stop here, as we already have a fully functioning app. We have an app that persists data, reads it and is publicly accessible online. This has been a great learning experience and once again, I stand by my initial statements on these Javascript frameworks. Angular, React or even Vue.js, they are all the same. You know one, you can easily pickup another.

As usual, if you find any of my posts useful support me by  buying or even trying one of my apps on the App Store. 

https://mydaytodo.com/apps/

Also, if you can leave a review on the App Store or Google Play Store, that would help too.

Categories: Javascript

0 Comments

Leave a Reply

Avatar placeholder

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