0

I am learning react and making a weather app. It is fetching API from open weather and assigns response to data and then setting value to city using setCity. But on first load the city varaible is undefined and when I console log out data it has all the JSON object.

const [city, setCity] = useState({})
  
  useEffect(()=>{
    getWeather()
  },[setCity])

  const getWeather = async ()=> {
    const reqAPI = `http://api.openweathermap.org/data/2.5/weather?q=toronto&units=metric&appid=${API_KEY}`

    const response = (await fetch(reqAPI)).json()
    const data = await response
    console.log(data)
    setCity(data)
    console.log(city)
  }

console log data is ok giving all value but city is undefined on first load and crashes the app. Please help as I am not able to find a solution

Here is the console output both should be same but are not!

You can see after editing out the code I am still getting this error

line 20 and 23 console output

3 Answers3

1

Fetch gives promise as output which needs to be handled with .then() i.e then() part is executed once promise once the promise is complete. Also add your state city to dependency array as stated below.

Update your useEffect like this

useEffect(() => {
    const reqAPI = `http://api.openweathermap.org/data/2.5/weather?q=toronto&units=metric&appid=${API_KEY}`;
    fetch(reqAPI)
        .then((response) => {
            console.log(response);
            response = response.json();
            console.log(response);
            setCity(response);
        })
        .catch((e) => console.log(e));
}, [city]);

and drop getWeather function.

Have a look at promises here


Update: Render your city component only if the city is not an empty object like this

{Object.keys(city).length !== 0 ? <YourCityComponent/> : <div></div>}

Here, i have added a check to not render when city is {} and in the above statement <YourCityComponent/> refers to your <City key={city.id} name={city.name} etc./>.

It can be done in multiple ways but this is the easiest on to understand. Have a look at this too on how to check if your object is empty.

Hemant
  • 1,127
  • 1
  • 10
  • 18
  • after editing the code to your way I understood the promise but then why I am getting this error in my component? App.js:33 Uncaught TypeError: Cannot read property 'name' of undefined Please check Question as I have adding new image – infamous_bad_habits Jan 15 '21 at 06:49
  • Can you share what is shown in console for consoles in line 20 and 22 of App.js? – Hemant Jan 15 '21 at 06:56
  • Yes please check the ediited last image! – infamous_bad_habits Jan 15 '21 at 07:00
  • The problem occurs in your components initial render i.e when city is `{}` it gives all keys as undefiend because city is empty object which changes to required object after useEffect is called. We need to handle it with a check. Will update my answer in a while.. – Hemant Jan 15 '21 at 07:13
  • I highly suggest you to try it on your own, as this is different problem than the OP's question. Rest assured i will update the answer. You should only render City component when city is non empty object check this https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object – Hemant Jan 15 '21 at 07:17
  • @infamous_bad_habits check the update part in my answer. – Hemant Jan 15 '21 at 08:50
  • After updating the code, City still is undefined and not rendering on the page! if I console log out after the useEffect I see a promise fulfilled and I can see code inside city but because the return is not async. It is not rendering on the screen – infamous_bad_habits Jan 16 '21 at 00:06
  • THANK YOU SO MUCH!!!!!! IT SOLVED MY QUESTION!!!!!!!!!!!!!!! I have been sleepless since 2 days!! your check to see if object is empty made my code work !!! – infamous_bad_habits Jan 16 '21 at 00:28
0

City being an empty object on the first load is exactly what is supposed to happen. Don't fix it, instead.. handle this state.

First, it's better to initialize the state with undefined:

const [city, setCity] = useState()

Then later in the component, handle this case:

return <div>Loading...</div>;
Evert
  • 93,428
  • 18
  • 118
  • 189
0

You should move your getWeather function to inside the useEffect hook. Also react state updates are asynchronous so when you're trying to console.log(city.main) right after you setCity there is no guarantee that the state has been updated. If you want to console.log when the state has been updated then you could use another useEffect with city in the dependency array.

Kyle Lambert
  • 369
  • 2
  • 13
  • Not working, tried putting that code inside and I understood it wont change the value right away but its not even getting assigned in return aswell! – infamous_bad_habits Jan 15 '21 at 06:36