Skip to content
Advertisement

React, make a second API call based on a state set by a first api call

I am trying to build a weather app with OpenWeather API. I have to get the latitude and longitude based on a first API call that define the location by search. I have tried async/await and useEffect hook but failed in both cases.

My code is below. What am I missing?

import { useState } from "react";

const api = {
  key: `${process.env.REACT_APP_API_KEY}`,
  base: "https://api.openweathermap.org/data/2.5/",
};

function App() {
  const [query, setQuery] = useState("");
  const [weather, setWeather] = useState({});
  const [location, setLocation] = useState({ lat: "", lon: "" });
  const [following, setFollowing] = useState([]);

  const search = async (e) => {
    if (e.key === "Enter") {
      await fetch(
        `${api.base}weather?q=${query}&units=metric&appid=${api.key}&lang=tr`
      )
        .then((res) => res.json())
        .then((result) => {
          setWeather(result);
          setQuery("");
          setLocation(result.coord);
          console.log(result);
          searchFollowing();
        });
    }
  };

  const searchFollowing = async () => {
    await fetch(
      `${api.base}onecall?lat=${location.lat}&lon=${location.lon}&units=metric&exclude=hourly,minutely&appid=${api.key}`
    )
      .then((res) => res.json())
      .then((result2) => {
        const array = result2.daily.slice(1, 6);
        console.log(following);
        setFollowing(array);
        // following == array
      });
  };

  const integer = (number) => {
    return Math.floor(Math.round(number));
  };

  const mapped = (following) => {
    following = [...following];
    return following.map((item, idx) => {
      const icon = item.weather[0].icon;
      const day = integer(item.temp.day);
      const night = integer(item.temp.night);
      return (
        <div key={idx} className="box">
          <img
            src={`http://openweathermap.org/img/wn/${icon}.png`}
            alt="weather"
            width={80}
            height={80}
          />
          <h3>Day {day} °C</h3>
          <h3>Night {night} °C</h3>
        </div>
      );
    });
  };

  const dateBuild = (d) => {
    let months = [
      "January",
      "February",
      "March",
      "April",
      "May",
      "June",
      "July",
      "August",
      "September",
      "October",
      "November",
      "December",
    ];
    let days = [
      "Sunday",
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday",
      "Saturday",
    ];

    let day = days[d.getDay()];
    let date = d.getDate();
    let month = months[d.getMonth()];
    let year = d.getFullYear();

    return `${day} ${date} ${month} ${year}`;
  };

  return (
    <div
      className={
        typeof weather.main !== "undefined"
          ? weather.main.temp > 25
            ? "App hot"
            : weather.main.temp < 25 && weather.main.temp > 5
            ? "App warm"
            : "App"
          : "App"
      }
    >
      <main>
        <div className="search-box">
          <input
            type="text"
            className="search-bar"
            placeholder="Search for a location..."
            onChange={(e) => setQuery(e.target.value)}
            onKeyPress={search}
            value={query}
          />
        </div>
        {typeof weather.main != "undefined" ? (
          <div>
            <div className="location-box">
              <div className="location">
                {weather.name}, {weather.sys.country}
              </div>
              <div className="date"> {dateBuild(new Date())}</div>
            </div>
            <div className="weather-box">
              <div className="temp">
                {Math.round(weather.main.temp)}°C
                <img
                  src={`http://openweathermap.org/img/wn/${weather.weather[0].icon.slice(
                    0,
                    2
                  )}d.png`}
                  alt="weather"
                  width={80}
                  height={80}
                />
              </div>
              <div className="weather">
                <p>
                  <span>Hissedilen</span>
                  {Math.floor(weather.main.feels_like)} °C
                </p>
                <p>
                  <span>Şu an</span>
                  {weather.weather[0].description}
                </p>
                <p>
                  <span>Basınç</span>
                  {weather.main.pressure} mb
                </p>
                <p>
                  <span>Rüzgar </span>
                  {Math.floor(weather.wind.speed)} km/h
                </p>
                <p>
                  <span>En fazla</span>
                  {Math.floor(weather.main.temp_max)} °C
                </p>
                <p>
                  <span>En az</span>
                  {Math.floor(weather.main.temp_min)} °C
                </p>
              </div>
            </div>
            <div className="followingdays">{mapped(following)}</div>
          </div>
        ) : (
          ""
        )}
      </main>
    </div>
  );
}

export default App;

Advertisement

Answer

What you are missing is that when you are calling searchFollowing(), React did not yet re-render, therefore location wouldn’t be updated. You could have an useEffect that would listen to location, and call searchFollowing() there when location.lat and location.lon are defined:

useEffect(()=>{
  if(location.lat && location.lon){
    searchFollowing();
  }
}, [location])

Then remove searchFollowing() call inside search function, as now you call it inside the above useEffect.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement