• Home
  • React – Simple weather App demo

React – Simple weather App demo

Today I will show you how to create simple weather app in React that will show you daily and hourly temperatures at your location. Our app will get weather and Geolocation data with OpenWeather Api and Google Geolocation API.

Demo is here, and source is on my GitHub here.

This app is part of my bigger (real world app) that I use in hotels where I work. (I work for Amadria Park Hotels, one of the biggest hotels group in Croatia ). This app is integrated in InfoPoint Displays in hotel lobbies and on Front Desk (reception). Main purpose is to show current Weather Forecast infos for our location alongside with other informations about hotel and destination that our guests need.

Here I will show you how to build only weather part. So let’s begin.

First create new react app in some folder of your choice. This is not beginner tutorial so I presume that you know how to create React app and that you have node.js and npm installed on your machine.

create-react-app chrisvz-react-weather

My test example is in folder named chrisvz-react-weather

After our new react app is created we must add some dependencies

npm i --save bootsrap
npm -i --save axios
npm i --save @fortawesome/fontawesome-svg-core \
npm i --save @fortawesome/free-solid-svg-icons \
npm i --save @fortawesome/react-fontawesome

Here we tell that our app will use bootstrap, axios and FontAwesome Icons

Next create two folders inside src. First folder name is components and second is services.

In components folder we will create next components:

  • Header.jsx
  • CurrentWeather.jsx
  • DailyWeather.jsx
  • HourlyWeather.jsx

In services folder we need to create two files

  • GeoLocationService.js – this service will get us current geolocation from Google Geolocation API. We will get coordinates from it ( Latitude and Longitude)
  • OpenWeatherService.js – this service is connected toOpenWeather Api and serve as an interface that allows us to obtain current weather, daily forecast, and hourly forecast information based on coordinates that is provided by GeoLocationService.js.

App.js

import React, { Component } from 'react';

import './App.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import Header from './components/Header';
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSun, faCircleNotch } from '@fortawesome/free-solid-svg-icons'
import MainWeatherPart from './components/MainWeatherPart'

library.add(faSun);
library.add(faCircleNotch);

class App extends Component {
  render() {
    return (
      <div className="App">
      <Header title=' Weather App - Amadria Park Hotels' />
      <div className="mt-lg-5">
            <div className="col-lg-6 p-0 mx-auto">
               <MainWeatherPart />
            </div>       
        </div>
      </div>
    );
  }
}

export default App;

Our App.js file is modified and here we import bootstrap.css, Header component, MainWeatherPart component and define usage of some FontAwesome Icons

Header.js

import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const Header = (props) => (
    <nav className="header navbar navbar-dark bg-light" style={{overflowX: 'hidden'}}>
        <div className="container">
            <div className="brand">
            <FontAwesomeIcon icon="sun" />
                <span className="brand-text">{props.title}</span>
            </div>
        </div>
    </nav>
);

Header.defaultProps = {
    title: 'React Weather App by Chrisvz'
};

Header.propTypes = {
    title: PropTypes.string
};

export default Header ;

 

MainWeatherPart.jsx

import React, { Component } from 'react';

import CurrentWeather from './CurrentWeather';
import DailyWeather from './DailyWeather';
import WeatherByHour from './WeatherByHour';

import { OpenWeatherService } from '../services/OpenWeatherService';
import { GeolocationService } from '../services/GeoLocationService';

const WeatherService = new OpenWeatherService();
const GeoLocationService = new GeolocationService();


class MainWeatherPart extends Component {

    constructor(props) {
        super(props);

        this.state = {
            showCurrentWeather: false,
            showDailyWeather: false,
            showHourlyWeather: false,
            weather: null,
            dailyForecasts: null,
            hourlyForecasts: null
        };

        this.handleOnRefresh = this.handleOnRefresh.bind(this);
    }


    componentDidMount() {
        GeoLocationService
            .getCurrentPosition()
            .then(position => { console.log(position);
                this.getMeCurrWeatherByPos(position);
                this.getMeDailyWeatherByPos(position);
                this.getMeHourlyWeatherByPos(position);
            })
            .catch(error => console.log(error));      
    }


    getMeCurrWeatherByPos(position) {
        
        if (!position) {
            throw Error('A valid position must be specified');
        }

        WeatherService
            .getOpenCurrentWeatherByPosition(position)
            .then(weather => { console.log(weather);
                this.setState(() => ({ weather: weather, showCurrentWeather: true }));
            })
            .catch(error => console.log(error));
    }


    getMeDailyWeatherByPos(position) {
        
        if (!position) {
            throw Error('A valid position must be specified');
        }

        WeatherService
            .getOpenDailyWeatherByPosition(position)
            .then(dailyForecasts => { console.log(dailyForecasts);
                this.setState(() => ({ dailyForecasts: dailyForecasts, showDailyWeather: true }));
            })
            .catch(error => console.log(error));
    }


    getMeHourlyWeatherByPos(position) {
        
        if (!position) {
            throw Error('A valid position must be specified');
        }

        WeatherService
            .getOpenHourlyWeatherByPosition(position)
            .then(hourlyForecasts => { console.log(hourlyForecasts);
                this.setState(() => ({ hourlyForecasts: hourlyForecasts, showHourlyWeather: true }));
            })
            .catch(error => console.log(error));
    }


    handleOnRefresh() {
        this.setState(() => ({
            showCurrentWeather: false,
            showDailyWeather: false,            
            showHourlyWeather: false
        }));

        GeoLocationService
            .getCurrentPosition()
            .then(position => {
                this.getMeCurrWeatherByPos(position);
                this.getMeDailyWeatherByPos(position);
                this.getMeHourlyWeatherByPos(position);
            })
            .catch(error => console.log(error));
    }


    showWeather() {
        return this.state.showCurrentWeather 
            && this.state.showDailyWeather 
            && this.state.showHourlyWeather;
            
    }


    render() {
        return (
            <div>
                {
                    this.showWeather() &&
                    <div>
                        <CurrentWeather weather={this.state.weather} onRefresh={this.handleOnRefresh} />
                        <p>&nbsp;</p>
                        <WeatherByHour hourlyForecasts={this.state.hourlyForecasts} location={this.state.weather.location.name} />
                        <p>&nbsp;</p>
                        <DailyWeather dailyForecasts={this.state.dailyForecasts} location={this.state.weather.location.name}/>
                    </div>
                }
                
            </div>
        );
    }
}


export default MainWeatherPart;

MainWeatherPart is main wrapper component for our app. Everything is here. We import other components and also services. In componentDidMoun we call our GeoLocationService and if everything is ok we get our coordinates. Based on current coordinates then we call three methods in our OpenWeatherService and we get 3 arrays with WeatherForecast Data from OpenWeather API. This arrays is send as props to our Child components (CurrentWeather, WeatherByHour, DailyWeather) and we then show this data infos as bootstrap cards.

CurrentWeather.jsx component. Inside component we parse data from weather props. Component show current city, temperature data and weather condition.

import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const getUpdateTime = (date) => {
    const hours = date.getHours().toString().padStart(2, '0');
    const minutes = date.getMinutes().toString().padEnd(2, '0');
    return `${hours}:${minutes}`;
};

const CurrentWeather = (props) => {
    
    const { weather } = props;
    
    return (
        <div style={{position: 'relative'}}>
            <div>{weather.location.name}</div>
            <div>MAX: {weather.temperature.maximum}&deg; | MIN: {weather.temperature.minimum}&deg;</div>
            <div>                
                <span >TEMP: {parseInt(weather.temperature.current)} &deg;&nbsp;<sup>c</sup></span>
            </div>
            <div>
                <img src={weather.icon} />
                <span>{weather.condition}</span>
            </div>            
            <div>Updated at {getUpdateTime(weather.date)}<br />&nbsp;</div>
            <button type="button" className="btn btn-light" onClick={props.onRefresh}><FontAwesomeIcon icon="circle-notch" /> Refresh Data</button>
        </div>
    );
};


CurrentWeather.propTypes = {
    onRefresh: PropTypes.func.isRequired,
    weather: PropTypes.object.isRequired
};


export default CurrentWeather;

WeatherByHour.jsx is component where we parse and show user weather forecast data up to next 36 hours.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const getTime = (date) => {
    return `${date.getHours()}:00`;
};

const getDate = (date) => {
    return `${weekday[date.getDay()]} ${date.getDate()}`;
};
const weekday = new Array(7);
weekday[0] = 'Sun';
weekday[1] = 'Mon';
weekday[2] = 'Tue';
weekday[3] = 'Wed';
weekday[4] = 'Thu';
weekday[5] = 'Fri';
weekday[6] = 'Sat';


class WeatherByHour extends Component {

    constructor(props){
        super(props);
    }

    

    render() {
        let styles1 = {
            width: "12rem",
            padding: "1rem"
          };
        
        return (
            <div className="d-flex align-content-center flex-wrap">

                   <div style={styles1}><p>Hourly temperatures in {this.props.location}</p></div>       
                
                {
                            !!this.props.hourlyForecasts && this.props.hourlyForecasts.map((fc, i) => (

                                <div style={styles1} key={"h"+i} >
                                  <div className="card">
                                  <small>{getDate(fc.date)} | {getTime(fc.date)}</small>
                                    <img className="icon mx-auto" src={fc.icon} />
                                    <div className="font-weight-bold">
                                        {parseInt(fc.temperature.current)}&deg;
                                    </div>
                                    <div className="text-capitalize">
                                        <small>{fc.condition}</small>
                                    </div>
                                  </div>
                                </div>

                            ))
                }
                
            </div>
        );
    }
}


WeatherByHour.propTypes = {
    hourlyForecasts: PropTypes.array.isRequired
};


export default WeatherByHour;

DailyWeather is component that show us Daily Weather Forecast for next 7 days.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const weekday = new Array(7);
weekday[0] = 'Sun';
weekday[1] = 'Mon';
weekday[2] = 'Tue';
weekday[3] = 'Wed';
weekday[4] = 'Thu';
weekday[5] = 'Fri';
weekday[6] = 'Sat';


const getDate = (date) => {
    return `${weekday[date.getDay()]} ${date.getDate()}`;
};


class DailyWeather extends Component {

    constructor(props){
        super(props);
    }

    

    render() {
        let styles1 = {
            width: "12rem",
            padding: "1rem"
          };
        
        return (
            <div className="d-flex align-content-center flex-wrap">

                   <div style={styles1}><p>Daily temperatures in {this.props.location}</p></div>       
                
                {
                            !!this.props.dailyForecasts && this.props.dailyForecasts.map((fc, i) => (

                                <div style={styles1} key={i} >
                                  <div className="card">
                                    <small>{getDate(fc.date)}</small>
                                    <img className="icon mx-auto" src={fc.icon} alt={fc.condition}/>
                                    <div className="font-weight-bold">
                                        {parseInt(fc.temperature.maximum)}&deg;
                                        &nbsp;<small>{parseInt(fc.temperature.minimum)}&deg;</small>
                                    </div>
                                    <div className="text-capitalize">
                                        <small>{fc.condition}</small>
                                    </div>
                                  </div>
                                </div>

                            ))
                }
                
            </div>
        );
    }
}


DailyWeather.propTypes = {
    dailyForecasts: PropTypes.array.isRequired
};


export default DailyWeather;

Before continuing, the following steps are required:

Without valid API keys this app won’t work. This API keys must be inserted in service files that we will create next.

GeoLocationService.js

import axios from 'axios';

const BASE_URL = 'https://www.googleapis.com/geolocation/v1/geolocate';
const API_KEY = ""; /* <- YOU MUST PUT YOUR GEOLOCATION API KEY HERE */

class GeolocationService {

    getCurrentPosition() {
        const url = `${BASE_URL}?key=${API_KEY}`;

        return new Promise((resolve, reject) => {
            axios
                .post(url, { considerIp: true })
                .then(response => {
                    if (response && response.status === 200) {
                        const { lat, lng } = response.data.location;
                        resolve({
                            latitude: lat,
                            longitude: lng
                        });
                    } else {
                        reject('Unable to retrieve current location');
                    }
                })
                .catch(error => {
                    const { errors } = error.response.data.error;
                    if (errors && errors.length > 0) {
                        errors.forEach(e => console.log(`Error: ${e.message}, Reason: ${e.reason}`));
                    }
                });
        });
    }
}

export { GeolocationService };

OpenWeatherService.js

import axios from 'axios';

const OPEN_WEATHER_BASE_URL = 'http://api.openweathermap.org/data/2.5';
const OPEN_WEATHER_API_KEY = ""; /* PUT YOUR OPEN WEATHER API KEY HERE */
const OPEN_WEATHER_IMG_URL = 'http://openweathermap.org/img/w';

const getOpenWeather= (url) => {
    return new Promise((resolve, reject) => {
        axios
            .get(url)
            .then(response => {
                if (response && response.status === 200) {
                    const { main, icon } = response.data.weather[0];
                    const { temp, temp_min, temp_max } = response.data.main;
                    const { lon, lat } = response.data.coord;
                    const { dt, name } = response.data;
                    resolve({
                        condition: main,
                        date: new Date(dt * 1000),
                        icon: `${OPEN_WEATHER_IMG_URL}/${icon}.png`,
                        location: {
                            name: name,
                            latitude: lat,
                            longitude: lon
                        },
                        temperature: {
                            current: temp,
                            minimum: temp_min,
                            maximum: temp_max
                        }
                    });
                } else {
                    reject('Open Weather data is not found');
                }
            })
            .catch(error => reject(error.message));
    });
};


const getOpenDailyWeather = (url) => {
    return new Promise((resolve, reject) => {
        axios
            .get(url)
            .then(response => {
                if (response && response.status === 200) {

                    const location = {
                        name: response.data.city.name,
                        latitude: response.data.city.coord.lat,
                        longitude: response.data.city.coord.lon
                    };                    

                    const dailyForecasts = response.data.list.map(fc => {
                        return {
                            condition: fc.weather[0].description,
                            date: new Date(fc.dt * 1000),
                            icon: `${OPEN_WEATHER_IMG_URL}/${fc.weather[0].icon}.png`,
                            location: location,
                            temperature: {
                                minimum: fc.temp.min,
                                maximum: fc.temp.max
                            }
                        };
                    });
                    
                    resolve(dailyForecasts);
                } else {
                    reject('Open Weather data is not found');
                }
            })
            .catch(error => reject(error.message));
    });
};


const getOpenHourlyWeather = (url) => {
    return new Promise((resolve, reject) => {
        axios
            .get(url)
            .then(response => {
                if (response && response.status === 200) {

                    const location = {
                        name: response.data.city.name,
                        latitude: response.data.city.coord.lat,
                        longitude: response.data.city.coord.lon
                    };                    

                    const hourlyForecasts = response.data.list.map(fc => {
                        return {
                            condition: fc.weather[0].description,
                            date: new Date(fc.dt * 1000),
                            icon: `${OPEN_WEATHER_IMG_URL}/${fc.weather[0].icon}.png`,
                            location: location,
                            temperature: {
                                current: fc.main.temp
                            }
                        };
                    });
                    
                    resolve(hourlyForecasts);
                } else {
                    reject('Open Weather data is not found');
                }
            })
            .catch(error => reject(error.message));
    });
};

class OpenWeatherService {

    getOpenCurrentWeatherByPosition({latitude, longitude}) {
        if (!latitude) {
            throw Error('Latitude is required');
        }

        if (!longitude) {
            throw Error('Longitude is required');
        }

        const url = `${OPEN_WEATHER_BASE_URL}/weather?appid=${OPEN_WEATHER_API_KEY}&lat=${latitude}&lon=${longitude}&units=metric`;
        
        return getOpenWeather(url);
    }


    getOpenDailyWeatherByPosition({latitude, longitude}) {
        if (!latitude) {
            throw Error('Latitude is required');
        }

        if (!longitude) {
            throw Error('Longitude is required');
        }

        const url = `${OPEN_WEATHER_BASE_URL}/forecast/daily?appid=${OPEN_WEATHER_API_KEY}&lat=${latitude}&lon=${longitude}&units=metric&cnt=7`;
        
        return getOpenDailyWeather(url);
    }


    getOpenHourlyWeatherByPosition({latitude, longitude}) {
        if (!latitude) {
            throw Error('Latitude is required');
        }

        if (!longitude) {
            throw Error('Longitude is required');
        }

        const url = `${OPEN_WEATHER_BASE_URL}/forecast?appid=${OPEN_WEATHER_API_KEY}&lat=${latitude}&lon=${longitude}&units=metric&cnt=12`;
        
        return getOpenHourlyWeather(url);
    }
}

export { OpenWeatherService };

Now if everything is created and saved you can test your app with npm start. I uploaded chrisvz-react-weather app to AWS so you can see demo here, and source is on my GitHub here.

 

Thanks for reading…

 

 

Tags:

Copyright by Kristijan Klepač 2018