Search code examples
javascriptreactjsvisual-studio-codeweathercodesandbox

React Weather simple app - works in codesandbox but not VSCode?


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!


Solution

  • 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.