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
.