visit
Almost 1 year ago I published an article about fetching data from an API in React.js.
But in that article, I used the class component and life cycle methods. After which, I received requests to create an article by using functional components along with React Hooks. This inspired me to think of a simple project idea that is compatible with this article.
In our webpage, we will have a form that takes city name as an input, and when we submit the form it sends a network request to the OpenWeatherMap API.
OpenWeatherMap resolves the request and as a response, it sends JSON data containing weather info about the city. After that, we will display the data on our webpage. Let’s take a look at the mock-up design for more clarification.
As we have the minimum info about what we are going to do and how it will look like, let’s start creating a react project.
In your terminal execute create-react-app <project name>
. I will call my project name react-hook-with-api
. It will set up a default project for us.
As usual, I will use my favorite Visual Studio Code as my editor. Now it’s time to do some cleanup.
Remove App.css
, App.test.js
, index.css
, rename App.js with App.jsx with the following code under components folder. Don’t forget to change the import of App in the index.js.
const App = () => {
return(
<div>Weather App</div>
)
}
export default App;
Execute yarn start
in your terminal, which will open your browser and you should see Weather App on the page.
I will use bootstrap
to make the designing easier. On your terminal execute yarn add bootstrap
if you are using yarn or with npm execute npm i bootstrap
. In your index.js don’t forget to add import ‘bootstrap/dist/css/bootstrap.min.css‘
. It is time to create our first component.
In our components folder create a SearchBar.jsx
file. Add the following lines of code in the file. And import the component in your App.jsx. On the webpage, you should see SearchBar.
// SearchBar.jsx
const SearchBar = ({ onSearchSubmit }) => {
return (
<div>
SearchBar
</div>
);
}
export default SearchBar;
// App.jsx
import SearchBar from './SearchBar';
const App = () => {
return(
<div>
<h3>Weather App</h3>
<SearchBar />
</div>
)
}
export default App;
Let’s add a form in our SearchBar component. Well, it’s going to be ugly. Because the priority is to utilize react hook. Replace your SearchBar.jsx with the following lines.
//SearchBar.jsx
const SearchBar = () => {
onFormSubmit = (event) => {
event.preventDefault();
}
onInputChange = (event) => {
console.log(event.target.value);
}
return (
<div className="w-80 mt-20">
<form className="w-100" onSubmit={onFormSubmit}>
<div className="w-60 m-auto d-flex justify-content-center">
<input
className="w-75"
type="text"
placeHolder="Enter city name"
onChange={onInputChange}
/>
<button type="submit" className="w-25">Search</button>
</div>
</form>
</div>
);
}
export default SearchBar;
In a previous article, I have talked about the State briefly. You can go through that to get a clear idea about when we need a state.
The first state we need is in the SearchBar
container to store the user input. To handle the operation of the state we will use the useState hook.
How to access the useState
hook?
How to use the useState
hook?
// Answer to the question No. 1
import { useState} from 'react';
// Answer to the question No. 2
const [value, setValue] = useState(0) // 0 is the default value
Here the value is the current state and setValue
is the function to update the value. And 0 is the default value that we pass as an argument in the useState
hook.
//SearchBar.jsx
import { useState } from 'react';
const SearchBar = () => {
const [cityName, setCityName] = useState('');
onFormSubmit = (event) => {
event.preventDefault();
}
onInputChange = (event) => {
setCityName(event.target.value);
}
return (
<div className="w-80 mt-20">
<form className="w-100" onSubmit={onFormSubmit}>
<div className="w-60 m-auto d-flex justify-content-center">
<input
className="w-75"
type="text"
placeHolder="Enter city name"
value={cityName}
onChange={onInputChange}
/>
<button type="submit" className="w-25">Search</button>
</div>
</form>
</div>
);
}
export default SearchBar;
As we got the user input now it’s time to get data according to that. As I mentioned I will be using OpenWeatherMap
service to get the data.
Click on your username and then click to generate one. Create a .env
file in the root directory of your project then add the following info in there.
REACT_APP_API_URL='//api.openweathermap.org/data/2.5'
REACT_APP_API_KEY=YOUR_API_KEY
It’s time to create a method in our App.jsx
file. Let’s name it as onSearchSubmit. There we will perform the API call based on the provided text.
Don’t forget to pass it as a prop into the SearchBar component. Then call it inside onFormSubmit
method.
//SearchBar.jsx
import { useState } from 'react';
const SearchBar = ({ onSearchSubmit}) => {
const [cityName, setCityName] = useState('');
onFormSubmit = (event) => {
event.preventDefault();
setCityName('');
onSearchSubmit(cityName);
}
onInputChange = (event) => {
setCityName(event.target.value);
}
return (
<div className="w-80 mt-20">
<form className="w-100" onSubmit={onFormSubmit}>
<div className="w-60 m-auto d-flex justify-content-center">
<input
className="w-75"
type="text"
placeHolder="Enter city name"
value={cityName}
onChange={onInputChange}
/>
<button type="submit" className="w-25">Search</button>
</div>
</form>
</div>
);
}
export default SearchBar;
//App.jsx
import './App.css'
import SearchBar from './SearchBar';
const App = () => {
const onSearchSubmit = async (text) => {
try {
await fetch(`${process.env.REACT_APP_API_URL}/weather?q=${text}&units=metric&appid=${process.env.REACT_APP_API_KEY}`)
.then(res => res.json())
.then(result => {
setData(result)
console.log(result);
});
} catch (err) {
console.error('err', err);
}
}
return(
<div className="ui container" style={{marginTop: '10px'}}>
<h3 className="text-center">Get Weather Info</h3>
<SearchBar onSearchSubmit={onSearchSubmit} />
</div>
)
}
export default App;
Great, now all we need to do is just store the value in a state, after that pass it in our displaying component. I will call that component Weather. Let’s create a file called Weather.jsx
and fill it with the following code.
//Weather.jsx
const Weather = ({ weatherData }) => {
return(
<div className="container w-50 mt-5 m-auto border border-secondary p-2">
{ weatherData && (
<>
<div className="d-flex justify-content-between">
<h6>Name </h6>
<small className="text-muted">{weatherData.name}</small>
</div>
<div className="d-flex justify-content-between">
<h6>Feels Like</h6>
<small className="text-muted">{weatherData.main.feels_like}</small>
</div>
</>
)}
{ !weatherData && (
<p className="text-center">Search by city name to get data</p>
)}
</div>
)
}
export default Weather;
And the final code of the App.jsx
will look like this:
// App.jsx
import { useState } from 'react';
import './App.css'
import SearchBar from './SearchBar';
import Weather from './Weather';
const App = () => {
const [data, setData] = useState(null);
const onSearchSubmit = async (text) => {
try {
await fetch(`${process.env.REACT_APP_API_URL}/weather?q=${text}&units=metric&appid=${process.env.REACT_APP_API_KEY}`)
.then(res => res.json())
.then(result => {
setData(result)
console.log(result);
});
} catch (err) {
console.error('err', err);
}
}
return(
<div className="ui container" style={{marginTop: '10px'}}>
<h3 className="text-center">Get Weather Info</h3>
<SearchBar onSearchSubmit={onSearchSubmit} />
<Weather weatherData={data} />
</div>
)
}
export default App;