DEV Community

Cover image for Day 10 of #100DaysOfCode — Building a Weather App
M Saad Ahmad
M Saad Ahmad

Posted on

Day 10 of #100DaysOfCode — Building a Weather App

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:

  1. User types a city name.
  2. User clicks "Search".
  3. The app fetches weather info for that city.
  4. 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;
Enter fullscreen mode Exit fullscreen mode

Here’s the flow:

useWeather.js Flow

  • Receives city as a parameter.
  • When city changes, a useEffect hook triggers.
  • If the city exists:

    • Start fetching weather data from the OpenWeatherMap API.
    • Set loading to true.
  • On success:

    • Store the JSON response in the weather state.
  • 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;
Enter fullscreen mode Exit fullscreen mode

Component Flow

  1. User types the city → stored in city (local state).
  2. When the user clicks Search, I update a second state called searchCity.
  3. Passing searchCity into useWeather(searchCity) triggers the API logic.
  4. 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:

weather_app

weather_app


🧠 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)