The following tutorial will outline and design a complete React weather application with Hooks from scratch. All resources are free to use and this post is friendly for all levels.
Unlike many weather application tutorials, we will push boundaries and go the extra mile to implement a custom keyword algorithm and give you a leading edge.
The user is able to request weather information from any given city in the world and depending on weather conditions, sends back images to reflect these conditions.
Note: This app is no-frills, meaning it won’t be a styled-up poodle by any means. However, the guideline is simplified and direct to provide a deeper focus on functionality and a step-by-step coding process. Feel free to style it as you see fit.
Alright. That’s it. So, get your coffee ready and let’s get started.
Internal Interactive Data Environment
Start by creating a React app by executing Node packages:
npx create-react-app weather-hooks
Next, enter the directory and run the development server.
cd weather-hooks
npm start
Open up the project folder in a text editor. Remove the unneeded files: App.test.js
and Logo.svg
. Now, create two new component files Form.js
, to build our weather searcher, and Weather.js
, to display our results for the user.
Go into App.js
and replace the unnecessary code and add an <h3>
tag title.
In the editor, your project folder should look something like this:
By now, your localhost server should be booted on your browser and returning the title “WEATHER APP” successfully.
Have a sip of coffee and let’s build our Form
component.
In Form.js
, we will create an arrow function called Form
that will take props as a parameter. Props will enable us to pass our API data from our app container. In this way, Form
is exported into our App.js
.
Set up a form tag and create two inputs and a button. One input will search for the city and the other will search for the country and the button submits both queries to the API.
Form.js
:
import React from 'react'
const Form = (props) => {
return (
<form onSubmit={props.getWeather}>
<input
type='text'
placeholder='city'
name='city'
/>
<input
type='text'
placeholder='country'
name='country'
/>
<button>Submit</button>
</form>
)
}
export default Form;
Form.js
Note that we set up our props to getWeather
which will be hooked up to a fetchData
function in our App.js
later on.
Continuing our component set up process, let’s go into Weather.js
and set up a Weather
function to receive data and display logic.
Thinking ahead about what basics our application will display, let’s build based on the premise: if a user types in a city and country and hits the search button to find weather information.
Back in the code, return the city and country info so that the user knows the data has been correctly matched along with the temperature and weather conditions and also an error catch message.
To set this up, we will also bring in props to our Weather
function and destructure them in this instance.
Weather.js
:
import React from 'react'
const Weather = ({description, city, country, error, temperature}) => {
return (
<div>
{city && country && <p>{city}, {country}</p>}
{temperature && <p>{temperature}</p>}
{description && <p> {description}</p>}
{error && <p>{error}</p>}
</div>
)
}
export default Weather;
Writing the name of our data followed by &&
before setting up our display is to ensure that the API has been passed on and true before proceeding to return the data.
The &&
operators only proceed if they are true so that this method comes in handy working without APIas a buffer from undefined errors.
Now that our components are set up, they remain inactive without state or visibility in our app container.
Before setting up our container and state, however, we need to access and set up the weather API. To do so, please proceed to https://home.openweathermap.org/users/sign_up
Sign up for free and create an account with email confirmation. Sign inand you can find your API key below, here:
Locate the relevant API for cities request at the bottom of the page:
You can find the API call we will use which includes city and country below:
Note: we will insert &APPID={APIKEY} to the end of our call to authenticate.
Excellent work. Now that we have our key and our call, drink some more coffee and let’s run some tests back in App.js
.
Back in App.js
, we will bring useState
to set up our Hooks in React along with our Form
component and Weather
component.
Then, we will set up our state to an empty array called weather
to set our weather data from the API. We input our API key by making use of backticks instead of quotations.
We’ll then create an asynchronous fetchData
function to retrieve weather data from and display a log in the Chrome DevTools.
The app container will then hook up the fetch function to the form and return the result.
App.js
should now be updated:
import React,{useState} from 'react';
import './App.css';
import Form from './Form';
import Weather from './Weather';
function App() {
const [weather,setWeather] = useState([])
const APIKEY = 'INSERT YOUR OWN KEY HERE'
async function fetchData(e) {
e.preventDefault()
const apiData = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=London,uk&APPID=${APIKEY}`)
.then( res => res.json())
.then(data => data)
setWeather({
data: apiData
}
)
}
return (
<div className="App">
<h3>WEATHER APP</h3>
<Form getWeather={fetchData} />
{console.log(weather.data)}
</div>
);
}
export default App;
Back in our localhost, data is being consoled when firing the event button.
Notice Name contains city name while sys property contains country name.
Although the console is displaying city weather information, our API call is hardcoded to London so that our form inputs remain inactive even if we change the input city.
To fix this and make the state more dynamic, target the city and country values by their elements on the form inputs and implement them into our API call.
Then, access in our setWeather
the pertaining properties to display in the Weather
component such as temperature, city, country, and description.
Finally, while drinking some more delicious brew, we can set up our data into our weather state and dynamically display the results of the weather to the user.
App.js
(updated):
import React,{useState} from 'react';
import './App.css';
import Form from './Form';
import Weather from './Weather';
function App() {
const [weather,setWeather] = useState([])
const APIKEY = '00517648ed782c3f434fed840bcfd50e'
async function fetchData(e) {
const city = e.target.elements.city.value
const country = e.target.elements.country.value
e.preventDefault()
const apiData = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city},${country}&APPID=${APIKEY}`)
.then( res => res.json())
.then(data => data)
setWeather({
data: apiData,
city: apiData.city,
country: apiData.sys.country,
description: apiData.weather[0].description,
temperature: apiData.main.temp,
error:""
}
)
}
return (
<div className="App">
<h3>WEATHER APP</h3>
<Form getWeather={fetchData} />
<Weather
city={weather.city}
country={weather.country}
description={weather.description}
temperature={weather.temperature}
error={weather.error}
/>
{console.log(weather.data)}
</div>
);
}
export default App;
Excellent. If we save and test the dev server by typing in a city and country and hit the submit button, we should be getting successful results as such:
And what may be lacking in style here, it makes up for with content.
Even though this is working, should we forget to type in a city and/or country and hit submit, the following error will occur:
To fix this, let’s apply the same &&
true condition to our setWeather
Hook and under error in the else section, we can include a notification for the user.
App.js
should now be updated to:
import React,{useState} from 'react';
import './App.css';
import Form from './Form';
import Weather from './Weather';
function App() {
const [weather,setWeather] = useState([])
const APIKEY = '00517648ed782c3f434fed840bcfd50e'
async function fetchData(e) {
const city = e.target.elements.city.value
const country = e.target.elements.country.value
e.preventDefault()
const apiData = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city},${country}&APPID=${APIKEY}`)
.then( res => res.json())
.then(data => data)
if(city && country) {
setWeather({
data: apiData,
city: apiData.city,
country: apiData.sys.country,
description: apiData.weather[0].description,
temperature: apiData.main.temp,
error:""
}
)} else {
setWeather({
data: '',
city: '',
country: '',
description: '',
temperature: '',
error:"Please Type A City And Country"
}
)}
}
return (
<div className="App">
<h3>WEATHER APP</h3>
<Form getWeather={fetchData} />
<Weather
city={weather.city}
country={weather.country}
description={weather.description}
temperature={weather.temperature}
error={weather.error}
/>
{console.log(weather.data)}
</div>
);
}
export default App;
And our error handling should now return in the event that a city or country has not been added to the input upon submitting the following:
OK. Nice job. Take a step back, enjoy the view (albeit minimally styled), and have some more coffee.
Looking back, so far we’ve managed to set up our API call and render it dynamic throughout our application. And, we have included error handling and without touching any class-based components.
This is a glimpse of React Hooks being awesome.
Before we finalize our application with a custom keyword algorithm and call this tutorial a day, let’s convert our temperature from Kelvin to Fahrenheit by applying the following conversion formula to our temperature property within our setWeather
Hook.
Update App.js
again to the following:
import React,{useState} from 'react';
import './App.css';
import Form from './Form';
import Weather from './Weather';
function App() {
const [weather,setWeather] = useState([])
const APIKEY = '00517648ed782c3f434fed840bcfd50e'
async function fetchData(e) {
const city = e.target.elements.city.value
const country = e.target.elements.country.value
e.preventDefault()
const apiData = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city},${country}&APPID=${APIKEY}`)
.then( res => res.json())
.then(data => data)
if(city && country) {
setWeather({
data: apiData,
city: apiData.city,
country: apiData.sys.country,
description: apiData.weather[0].description,
temperature: Math.round(apiData.main.temp * 9/5 - 459.67),
error:""
}
)} else {
setWeather({
data: '',
city: '',
country: '',
description: '',
temperature: '',
error:"Please Type A City And Country"
}
)}
}
return (
<div className="App">
<h3>WEATHER APP</h3>
<Form getWeather={fetchData} />
<Weather
city={weather.city}
country={weather.country}
description={weather.description}
temperature={weather.temperature}
error={weather.error}
/>
{console.log(weather.data)}
</div>
);
}
export default App;
Note: Math.round
method calculates and rounds our conversion formula to relieve the user of unnecessary decimal values in our Weather
display.
Back in our Weather.js
file, we can add a few more UX touch-ups by specifying attributes to our weather description and temperature, implementing the following additions to our return:
import React from 'react'
const Weather = ({description, city, country, error, temperature}) => {
return (
<div>
{city && country && <p>{city}, {country}</p>}
{temperature && <p>{temperature} °F</p>}
{description && <p> Conditions: {description}</p>}
{error && <p>{error}</p>}
</div>
)
}
export default Weather;
If we now type in Miami and United States into our input, we should receive the following compiled results:
Much better. The temperature is much more pleasing from a user experience standpoint. Before we move on to the final steps, it could not be a more appropriate time to relax and enjoy some warm and refreshing life lifting coffee.
Now that the application is dynamically sending and receiving our API call for the user, it can be considered a useful tool. However, this is not the first weather app, nor likely the last one ever to be built…
So, how can we make this stand out? The question is: “How can we take this foundation and working system to the next level?”
And questions like that are the keys to thinking as a standout developer.
Firing up our now coffee-inspired imaginations, what if we could come up with a way to take our weather data description and, based on that return, some kind of media content for the user as well?
For example, we can return an image of clouds whenever the description of our weather API is cloudy.
Although this process may appear simple enough on the surface, it demonstrates an ability and curiosity to think in terms of augmenting possibilities given our set of tools. Very cool.
To convert this conception into JavaScript that can run in React, let’s write an algorithm that searches for keywords and when a match is found returns an image.
By iterating through our weather description strings and using the includes
JavaScript method to check for matches in a new keyword array of strings, this can be accomplished.
Since we’re searching for unique strings and not combinations (words), we will also use the split
method to split up the weather strings into an array of unique strings.
Let’s code this out and set up consoles for both arrays as well as a console for our match.
Weather.js
file should now be updated as follows:
import React from 'react'
const Weather = ({description, city, country, error, temperature}) => {
if(description) {
const weatherDescription = description.split(' ')
const keyWords = ['cloudy','clouds', 'cloud', 'overcast']
for(let i = 0; i < weatherDescription.length; i++) {
if(keyWords.includes(weatherDescription[i])) {
console.log(weatherDescription[i], ': we have a match')
}
}
console.log(keyWords)
console.log(weatherDescription)
}
return (
<div>
{city && country && <p>{city}, {country}</p>}
{temperature && <p>{temperature} °F</p>}
{description && <p> Conditions: {description}</p>}
{error && <p>{error}</p>}
</div>
)
}
export default Weather;
Now, save and test the results.
Search for a city that has “cloud” or “cloudy” as a description. In your console, you should be seeing a match returned as well as both your keyword and weather description arrays being displayed:
Excellent. This means it’s working.
Finally, let’s go ahead and script our code into a function that we can call in our weather app.
Then, let’s set up the function to return an HTTPS JPG image of clouds with<img src />
.The function will render an image of clouds whenever the API data contains matching keywords of clouds.
Weather.js
should now be updated to the following:
import React from 'react'
const Weather = ({description, city, country, error, temperature}) => {
function matchValues () {
if(description) {
const weatherDescription = description.split(' ')
const keyWords = ['cloudy','clouds', 'cloud', 'overcast']
for(let i = 0; i < weatherDescription.length; i++) {
if(keyWords.includes(weatherDescription[i])) {
return <img src='https://media.freestocktextures.com/cache/74/8b/748ba3fe5976d8b03219a64851d2790d.jpg' />
}
}
}}
return (
<div>
{city && country && <p>{city}, {country}</p>}
{temperature && <p>{temperature} °F</p>}
{description && <p> Conditions: {description}</p>}
{error && <p>{error}</p>}
{description && matchValues()}
</div>
)
}
export default Weather;
Save and search again for a city with clouds in its description back in our application, which will produce the following:
And there it is. Not only is our application sending updated weather data to our users from any city in the world, it’s also returning media content based on data it’s receiving.
In this way, we bridge functionality with creative experiences, augmenting an overall unique user experience.
But this is just the very beginning. If you wanted to, for example, render a sunny picture when the description is sunny, think about how you could modify and implement the code to do so. That can even be your exercise upon completion.
The takeaway here isn’t just building a weather app with Hooks from the ground up, but realizing the potential of just how high from the ground one can climb by combining skills and thoughtful vision.
And most importantly, nicely done for making it this far.
That’s it. Please find the source links to the full video tutorial as well as GitHub repository at the top for more extensive documentation.
And, if you still have any questions please check out some additional resources below and/or feel free to drop me a message or comment.
#react #reactjs #React Hooks #javascript