ISE Blog

How To Create a Single-Page Application Using React

If you have been involved with web development recently, you have most likely heard about single-page applications (SPA).  SPAs are typically javascript and run entirely in your browser.  As you interact with the elements on the screen, the application requests any data it needs from APIs and rewrites the current page.  Since the application is not having to request each page from the server, the request times are shorter, and the data transferred is significantly smaller than that of a non-SPA web application. 

One of the popular frameworks out there is called React.  React was created by employees at Facebook as a replacement for Facebook’s newsfeed.  Since then, it has become one of the most commonly used frameworks for creating new web applications.

If you’re wondering how to get started with React, you’ve come to the right place.  I’ll guide you through creating a sample application which requests weather data and displays a daily forecast to the user.  We will start by making a Hello World project.  Then we will modify that project to retrieve and display the weather forecast.  I’ll be using npm to manage the project dependencies and Material-UI to help make the sample application a bit prettier.  I won’t be going into detail on either of them here, but feel free to look at them a bit more on your own if you are interested.

Setup

Before venturing through this sample project, there are a couple of setup steps.  First, you will need to install npm.  Npm is packaged with nodejs and can be downloaded from https://nodejs.org/en/download/.

The second setup step involves signing up for an OpenWeatherMap account and generating an API key.  OpenWeatherMap is a site containing a collection of weather APIs for historical, current, and forecast weather data.   Many of the APIs can be accessed from a free account.

Objective

Recently I've been riding my bike to work quite often, but I prefer not to ride in the rain.  I typically look at the weather for the next couple days, so I know which mornings I’ll need to prepare for biking vs. driving.  This use case seemed like a perfect topic for my sample application.

The objective is to build a simple application which displays the weather forecast of a certain location for the next few days.  I want to be able to specify a location by zip code and have the application display the expected weather:  sunny, cloudy, thunderstorm, etc.  Here is a super fancy mock-up of the desired outcome:

Weather React Mockup

Step 1

First, we want to setup the folder structure and create a Hello World application to make sure everything is situated correctly.  Create a directory for your new application and add the following package.json file into it.

package.json

{
  "name": "weather-react",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^1.4.3",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

 

Next, we will want to create a couple subfolders to store our React application.  Create the folders public and src so your folder structure now looks like:

weather-react/

├── public/

├── src/

└── package.json

Next, copy the manifest.json, index.html, and favicon.ico files into the public folder.  The manifest tells mobile devices details about the website, allowing the site to be “installed” on a device.  Index.html is the html container for the application.  It provides a div element which will be the container for the React DOM. 

manifest.json

{
    "short_name": "Weather App",
    "name": "React Weather App",
    "icons": [
      {
        "src": "favicon.ico",
        "sizes": "64x64 32x32 24x24 16x16",
        "type": "image/x-icon"
      }
    ],
    "start_url": "./index.html",
    "display": "standalone",
    "theme_color": "#000000",
    "background_color": "#ffffff"
}

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

<title>Weather App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>

 

favicon.ico

favicon-1

Lastly, we need to copy the index.js code into the src folder.  The index.js file contains a Hello World React component and renders it in place of the root div in index.html. 

index.js

import React from 'react';
import ReactDOM from 'react-dom';


class HelloWorld extends React.Component {

  render() {
    return (
      <div>
          Hello World!
      </div>
    );
  }
}



// ========================================

ReactDOM.render(
<HelloWorld />,
document.getElementById('root')
);

 

At this point your folder structure should look like:

weather-react/

├── public/

│   ├── favicon.ico

│   ├── index.html

│   └── manifest.json

├── src/

│   └── index.js

└── package.json

At this point we should be able to install and run the application.  You can do this by running npm install to install the project dependencies and then npm start to start the React dev server.  A chrome window should automatically open, but if not navigate to http://localhost:3000 to view your application.  You should see “Hello World!” on a plain background. 

If you had any problems with the steps above, or just want an easier way to copy the files, the complete project structure can be found on branch step-1-create-base-project in my GitHub weather-react repository.

Step 2

Now that we have a working React application, let’s add some logic to retrieve some weather data and display it.  During this step you will need to have your OpenWeatherMap API key handy. 

Let’s start by adding a file called weather.js to the src folder which will be the main component for our weather forecast app.  Place your API key in the first line of the ‘Weather’ class where the placeholder currently sits. 

weather.js

import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import { TextField } from '../node_modules/@material-ui/core';
import Grid from '@material-ui/core/Grid';
import ForecastCard from './forecastCard';
import Button from '@material-ui/core/Button';

const styles = theme => ({
    root: {
      flexGrow: 1,
    },
    paper: {
      height: 140,
      width: 100,
    },
    gridRow: {
        marginTop: 50
    }
});


class Weather extends React.Component {
    // This API_KEY should be an active api key from openweathermap.org.  A free account API key should suffice.
    static API_KEY = '__REPLACE_ME_WITH_YOUR_API_KEY__';

    constructor() {
        super();
        this.state = {
            forecasts: [],
            zipCode: '52241'
        };
        this.refreshForecast = this.refreshForecast.bind(this);
    }

    componentDidMount() {
        this.refreshForecast();
    }

    refreshForecast() {
        fetch('https://api.openweathermap.org/data/2.5/forecast?zip=' + this.state.zipCode + '&appid=' + Weather.API_KEY)
            .then(results => {
                return results.json();
            })
            .then(data => { 
                let truncatedData = data.list.filter(entry => entry.dt_txt.includes("12:00:00"));
                let forecasts = [];

                truncatedData.forEach((element, index) => {
                    let day = (new Date(element.dt_txt)).getDay();
                    let currentElementWeather = element.weather[0];
                    let dailyForecast = {'key': index, 'day': day, 'weather': currentElementWeather.main, 'icon': currentElementWeather.icon}
                    forecasts.push(dailyForecast);
                });
                this.setState({forecasts: forecasts});
            })
    }

    updateZipCode(evt) {
        this.setState({
            zipCode: evt.target.value
        });
    }

    render() {
      return (
        <React.Fragment>
        <Typography variant="display4" align="center">
            Forecast
        </Typography>
            
        <Grid container spacing={16} className={this.props.classes.root}>
            <Grid item xs={12} className={this.props.classes.gridRow}>
                <Grid container justify="center">
                    <TextField
                        label="Zip Code"
                        value={this.state.zipCode}
                        onChange={evt => this.updateZipCode(evt)}/>
                </Grid>
            </Grid>
            <Grid item xs={12}>
                <Grid container justify="center">
                    <Button variant="contained" color="primary" className={this.props.classes.button} onClick={this.refreshForecast}>
                        Refresh
                    </Button>
                </Grid>
            </Grid>
            <Grid item xs={12} className={this.props.classes.gridRow}>
                <Grid container justify="center" spacing={16}>
                    {this.state.forecasts.map(value => (
                    <Grid key={value.key} item>
                        <ForecastCard day={value.day} weather={value.weather} value={value.key} icon={value.icon} />
                    </Grid>
                    ))}
                </Grid>
            </Grid>
        </Grid>

        </React.Fragment>
      );
    }
}

Weather.propTypes = {
    classes: PropTypes.object.isRequired
};

export default withStyles(styles)(Weather);

 

Let’s talk about what’s going on in weather.js for a moment.  The Weather class’s render method displays a heading, a text field for inputting the zip code, and a refresh button to refresh the forecast.  When the refresh button is clicked, the app reaches out to the OpenWeatherMap forecast api to retrieve weather forecasts for the next five days.  Now, due to the daily forecast API not being available to free accounts, I used the 5 day / 3 hour forecast and selected the forecast for Noon GMT of each day, which is approximately the time someone would be riding their bike into work in the morning.  It’s not a perfect solution, but it is good enough for our app. 

Once the data has been retrieved, Weather then populates ForecastCards with the forecast data.  This means our next step is to create the ForecastCard component.  Copy the following forecastCard.js file into the src folder.

forecastCard.js

import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';

const styles = theme => ({
    root: {
      flexGrow: 1,
    },
    paper: {
      width: 100,
    },
    dayHeading: {
        paddingBottom: 0,
        paddingTop: 8
    },
    forecastImage: {
        padding: 0
    }
});

class ForecastCard extends React.Component {

    retrieveDayName(dayNumber) {
        let dayArray = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
        return dayArray[dayNumber];
    }

    retrieveIconURL(icon) {
        return "http://openweathermap.org/img/w/" + icon + ".png";
    }

    render() {
        return (
            <React.Fragment>
                <Paper className={this.props.classes.paper} >
                    <Grid container className={this.props.classes.root}>
                        <Grid item xs={12} className={this.props.classes.dayHeading}>
                            <Typography variant="subheading" align="center">
                                {this.retrieveDayName(this.props.day)}
                            </Typography>
                        </Grid>
                        <Grid item xs={12} className={this.props.classes.forecastImage}>
                            <Grid container justify="center">
                                <img src={this.retrieveIconURL(this.props.icon)} alt={this.props.weather} height="100" width="100" />
                            </Grid>
                        </Grid>
                        <Grid item xs={12}>
                            <Typography variant="subheading" align="center">
                                {this.props.weather}
                            </Typography>
                        </Grid>
                    </Grid>
                </Paper>
            </React.Fragment>
        );
    }
}

ForecastCard.propTypes = {
    classes: PropTypes.object.isRequired
};

export default withStyles(styles)(ForecastCard);

 

The ForecastCard class displays a single day’s forecast information on a Material-UI paper component. 

We now have all the logic in place to retrieve and display the information, but if we were to run the application right now, we would still see “Hello World!”  We need to modify index.js to replace the root div with a Weather component instead of the HelloWorld component.  This can be done by copying the following index.js file over our current one in the src folder.

index.js

import React from 'react';
import ReactDOM from 'react-dom';

import Weather from './weather';

// ========================================

ReactDOM.render(
  <Weather />,
  document.getElementById('root')
);

 

Your final folder structure should look like:

weather-react/

├── public/

│   ├── favicon.ico

│   ├── index.html

│   └── manifest.json

├── src/

│   ├── forecastCard.js

│   ├── index.js

│   └── weather.js

└── package.json

Running npm start now will render forecast application, displaying the following screen:

Forecast ScreenOnce again, I have provided the complete code on branch step-2-create-forecast-logic in my GitHub weather-react repository.

Congratulations!  

You have created your first React application.  We have touched on just a few of the many capabilities of React, but hopefully this gives you a bit of knowledge to use when starting to build your own application.


Do you have any questions on building applications using React?  Join in the conversation below.  And as always, feel free to contact us if you're interested in learning more about how we can solve your team's challenges, or if you're interested in joining our team!

Zach Bodensteiner, Senior Software Engineer

Zach Bodensteiner, Senior Software Engineer

Between matches of ping pong in the break room, Zach Bodenteiner is a Senior Software Engineer, Practice Lead of Mobile Development, and Team Lead at ISE. He graduated from the University of Iowa with a degree in Computer Engineering in 2012, and joined ISE shortly thereafter. When he is not growing his skills at the office, he can be found participating in local pub trivia, doing yard work/gardening or playing with his dog, Millie.

Zach Bodensteiner, Senior Software Engineer

Latest posts by Zach Bodensteiner, Senior Software Engineer (see all)

How To Create a Single-Page Application Using React Aug 09, 2018

5 Things I Learned at Internet of Things World May 20, 2016