For Day 10 of my #100DaysOfCode journey, I wanted to build something practical using the React concepts I’ve learned so far — useState, useEffect, and fetching data from APIs.
So I decided to create a simple Weather App using the free API from OpenWeatherMap.
The goal:
👉 User enters a city → Clicks search → Real-time weather data appears.
This project helped me understand how to structure API logic properly using a custom hook, and how to keep the UI clean by separating concerns.
🌤️ What I Built: A Weather App Using a Custom React Hook
The app flow is straightforward:
- User types a city name.
- User clicks "Search".
- The app fetches weather info for that city.
- UI updates to show either:
- Loading state
- Error (if the city doesn’t exist)
- Weather data (if successful)
To keep things clean and reusable, I moved the entire API-fetching logic into a custom hook called useWeather.js.
Creating the Custom Hook — useWeather.js
The custom hook manages everything related to fetching data.
// useWeather.jsx
import { useState, useEffect } from "react";
const useWeather = (city) => {
const API_key = "Your_API_Key";
const [weather, setweather] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!city) return;
async function fetchWeather() {
setLoading(true);
setError(null);
try {
const respone = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_key}&units=metric`,
);
if (!respone.ok) throw new Error("City not found");
const data = await respone.json();
setweather(data);
console.log(data);
} catch (error) {
setError(error.message);
setLoading(false);
} finally{
setLoading(false);
}
}
fetchWeather();
}, [city]);
return { weather, loading, error };
};
export default useWeather;
Here’s the flow:
useWeather.js Flow
- Receives
cityas a parameter. - When
citychanges, auseEffecthook triggers. -
If the city exists:
- Start fetching weather data from the OpenWeatherMap API.
- Set
loadingto true.
-
On success:
- Store the JSON response in the
weatherstate.
- Store the JSON response in the
-
On failure:
- Capture the error message.
Finally, return
{ weather, loading, error };Any component using this hook instantly gets real-time data updates whenever the city changes.
This approach keeps all API logic separate and makes the entire weather-fetching system reusable for any component.
Building the UI — Weather.jsx
Once the hook was ready, I created the weather component that interacts with it.
// Weather.jsx
import { useState } from "react";
import useWeather from "../weatherHook/useWeather";
const Weather = () => {
const [city, setCity] = useState("");
const [searchCity, setSearchCity] = useState("");
const { weather, loading, error } = useWeather(searchCity);
function handleSubmit(e) {
e.preventDefault();
setSearchCity(city);
}
return (
<>
<h1 className="text-3xl font-bold text-center">Weather App</h1>
<br />
<div>
<form onSubmit={handleSubmit}>
<input
value={city}
onChange={(e) => setCity(e.target.value)}
type="text"
placeholder="Enter city name"
className="border-2 border-gray-400 rounded-md p-2 w-60 mx-auto block"
/>
<button className="bg-blue-500 text-white rounded-md p-2 mt-2 w-40 mx-auto block">
Search
</button>
</form>
</div>
{loading && <p className="text-3xl text-center font-extrabold">Loading....</p>}
{error && <p className="text-red-500 text-3xl text-center font-extrabold">Error: {error}</p>}
{weather && (
<div className="text-center text-2xl font-semibold mt-4">
<h2>Weather: {weather.name}, {weather.sys.country}</h2>
<p>Temperature: {weather.main.temp}℃</p>
<p>Weather: {weather.weather[0].main}</p>
</div>
)}
</>
);
};
export default Weather;
Component Flow
- User types the city → stored in
city(local state). - When the user clicks Search, I update a second state called
searchCity. - Passing
searchCityintouseWeather(searchCity)triggers the API logic. - The component displays:
- “Loading…” when fetching
- An error message if something goes wrong
- Weather results when the data comes in
Every time the user searches for a new city, the cycle repeats.
This separation of responsibilities made the app much cleaner:
- UI component = handles input + displaying data
- Custom hook = handles API logic
Final Result:
🧠 What I Learned (Day 10 Takeaways)
Here are my biggest lessons from this mini-project:
✔ Custom hooks simplify API logic
Moving fetch logic into a hook keeps components focused on UI.
✔ useEffect + dependency arrays are powerful
By watching for changes in city, the hook automatically re-fetches data whenever needed.
✔ Always manage loading and error states
They make the UX smoother and help users understand what's happening.
✔ Breaking a project into small flows improves clarity
Defining flow diagrams before writing code sped up development.
Wrapping Up
This was Day 10 of my 100 Days of Code challenge, and building a Weather App was the perfect way to practice real-world React concepts.
If you're learning React, I highly recommend building small tools like this. They help you understand the flow between inputs, state changes, and API calls in a very intuitive way.
Happy coding!


Top comments (0)