I am making a beginner React weather app with Geolocation. Here is the code. The main code is in App.js. Daily and Hourly are for displaying only.
App.js
import React, { useState, useEffect } from "react";
import Hourly from "./Components/Hourly";
import Daily from "./Components/Daily";
const App = () => {
const [currentWeatherOne, setCurrentWeatherOne] = useState({});
const [currentWeatherTwo, setCurrentWeatherTwo] = useState({});
const [currentWeatherThree, setCurrentWeatherThree] = useState({});
const [currentWeatherFour, setCurrentWeatherFour] = useState({});
const [currentWeatherFive, setCurrentWeatherFive] = useState({});
const [lat, setLat] = useState();
const [long, setLong] = useState();
const [today, setToday] = useState();
const [tomorrow1, setTomorrow1] = useState({});
const [tomorrow2, setTomorrow2] = useState({});
const [refreshText, setRefreshText] = useState();
const getPosition = () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve);
});
};
const getWeather = async (lat, long) => {
let a = await fetch(
`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${long}&appid=dcadf332823cddfb979926a1414274e8&units=metric`
);
let b = await a.json();
console.log(b);
setCurrentWeatherOne(b.list[1]);
setCurrentWeatherTwo(b.list[2]);
setCurrentWeatherThree(b.list[3]);
setCurrentWeatherFour(b.list[4]);
setCurrentWeatherFive(b.list[5]);
setToday(b.list[0]);
setTomorrow1(b.list[11]);
setTomorrow2(b.list[19]);
};
useEffect(() => {
getPosition().then(data => {
setLat(data.coords.latitude);
setLong(data.coords.longitude);
getWeather(data.coords.latitude, data.coords.longitude);
});
}, []);
// console.log(currentWeatherOne, currentWeatherThree, currentWeatherFive);
const refresh = () => {
getWeather(lat, long);
let hours = String(new Date().getHours());
let minutes = String(new Date().getMinutes());
let seconds = String(new Date().getSeconds());
setRefreshText(
`Updated as of: ${hours}:${minutes.length < 2 ? "0" : ""}${minutes}:${
seconds.length < 2 ? "0" : ""
}${seconds}`
);
};
return (
<div className="App">
<header>Geolocation weather</header>
<button onClick={refresh}>Refresh</button>
<br />
{refreshText}
<hr />
<h3>Hourly</h3>
<div className="container-2">
<Hourly
icon={currentWeatherOne.weather[0].icon}
time={currentWeatherOne.dt_txt}
minTemp={currentWeatherOne.main.temp_min}
maxTemp={currentWeatherOne.main.temp_max}
description={currentWeatherOne.weather[0].description}
humidity={currentWeatherOne.main.humidity}
/>
<Hourly
icon={currentWeatherTwo.weather[0].icon}
time={currentWeatherTwo.dt_txt}
minTemp={currentWeatherTwo.main.temp_min}
maxTemp={currentWeatherTwo.main.temp_max}
description={currentWeatherTwo.weather[0].description}
humidity={currentWeatherTwo.main.humidity}
/>
<Hourly
icon={currentWeatherThree.weather[0].icon}
time={currentWeatherThree.dt_txt}
minTemp={currentWeatherThree.main.temp_min}
maxTemp={currentWeatherThree.main.temp_max}
description={currentWeatherThree.weather[0].description}
humidity={currentWeatherThree.main.humidity}
/>
<Hourly
icon={currentWeatherFour.weather[0].icon}
time={currentWeatherFour.dt_txt}
minTemp={currentWeatherFour.main.temp_min}
maxTemp={currentWeatherFour.main.temp_max}
description={currentWeatherFour.weather[0].description}
humidity={currentWeatherFour.main.humidity}
/>
<Hourly
icon={currentWeatherFive.weather[0].icon}
time={currentWeatherFive.dt_txt}
minTemp={currentWeatherFive.main.temp_min}
maxTemp={currentWeatherFive.main.temp_max}
description={currentWeatherFive.weather[0].description}
humidity={currentWeatherFive.main.humidity}
/>
</div>
<h3>Daily</h3>
<div className="container-1">
<Daily
icon={today.weather[0].icon}
time={today.dt_txt}
minTemp={today.main.temp_min}
maxTemp={today.main.temp_max}
description={today.weather[0].description}
humidity={today.main.humidity}
/>
<Daily
icon={tomorrow1.weather[0].icon}
time={tomorrow1.dt_txt}
minTemp={tomorrow1.main.temp_min}
maxTemp={tomorrow1.main.temp_max}
description={tomorrow1.weather[0].description}
humidity={tomorrow1.main.humidity}
/>
<Daily
icon={tomorrow2.weather[0].icon}
time={tomorrow2.dt_txt}
minTemp={tomorrow2.main.temp_min}
maxTemp={tomorrow2.main.temp_max}
description={tomorrow2.weather[0].description}
humidity={tomorrow2.main.humidity}
/>
</div>
</div>
);
};
export default App;
Daily
import React from "react";
const Daily = ({ icon, time, minTemp, maxTemp, description, humidity }) => {
let timeToShow = time.split(" ")[0];
let one = timeToShow.split("-")[0];
let two = timeToShow.split("-")[1];
let three = timeToShow.split("-")[2];
maxTemp = Math.floor(maxTemp);
minTemp = Math.floor(minTemp);
description = description
.split(" ")
.map(word => {
return word[0].toUpperCase() + word.substring(1);
})
.join(" ");
return (
<div className="weather">
<div>
{three}-{two}-{one}
</div>
<img
src={`https://openweathermap.org/img/w/${icon}.png`}
alt="weather icon"
/>
<div>
<div>
<span className="temp">{maxTemp}</span>
<span>{minTemp}</span>
</div>
<br />
<div>{description}</div>
<br />
{humidity}% humidity
</div>
</div>
);
};
export default Daily;
Hourly
import React from "react";
const Hourly = ({ description, humidity, time, icon, minTemp, maxTemp }) => {
let timeToShow = time.split(" ")[1];
let first = timeToShow.split(":")[0];
let second = timeToShow.split(":")[1];
maxTemp = Math.floor(maxTemp);
minTemp = Math.floor(minTemp);
description = description
.split(" ")
.map(word => {
return word[0].toUpperCase() + word.substring(1);
})
.join(" ");
return (
<div className="weather">
<div>
{first}:{second}
</div>
<img
src={`https://openweathermap.org/img/w/${icon}.png`}
alt="weather icon"
/>
<div>
<div>
<span className="temp">{maxTemp}</span>
<span>{minTemp}</span>
</div>
<br />
<div>{description}</div>
<br />
{humidity}% humidity
</div>
</div>
);
};
export default Hourly;
On codesandbox, sometimes it does not work, so what I have to do is comment all the Daily and Hourly components, wait 2 seconds for codesandbox to re-load, and uncomment them and generally it will work.
Not so on VSCode. It will say that currentWeatherOne.weather is undefined which means currentWeatherOne tries to access the information that is not there yet. I don't understand, isn't this the whole point of using a promise? Using async, await etc? So that it waits for the result to be given back, then display it? Whatever the reason, why does it work on codesandbox and not VSCode?
Would really love to get feedback and help. Thank you all!
currentWeatherOne is set to just empty object {}
initially and you are trying to access currentWeatherOne.weather[0].icon
in the return section. since weather
is not available in currentWeatherOne
yet, you are getting this error.
UseEffect is called after the first render so your getWeather
to fetch the weather info is called after the initial render. Until that time, currentWeatherOne
is just empty object.
Have fixed the issue here - https://codesandbox.io/s/tender-wood-b4kuh
Hope this helps.