Reputation: 127
I am working on a weather app project. The problem is I cannot reach the name property in the API call.
Here is the API response:
{cod: '200', message: 0, cnt: 40, list: Array(40), city: {…}}
city: {id: 745042, name: 'Istanbul', coord: {…}, country: 'TR', population: 11581707, …}
cnt: 40
cod: "200"
list: (40) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
message: 0
[[Prototype]]: Object
Here is my ApiCall component:
import axios from "axios"
import { useEffect, useState } from "react"
function ApiCall({ getCities }) {
const[data, setData] = useState({})
useEffect(() => {
axios(`https://api.openweathermap.org/data/2.5/forecast?q=Istanbul&appid=c681e6e33ec339728fdf88e0b24a2a01`)
.then(res => console.log(res.data))
.catch(err=> console.log(err))
})
const { city, list } = data
console.log(city.name)
Finally, this is the error I get:
Uncaught TypeError: Cannot read properties of undefined (reading 'name')
Upvotes: 0
Views: 42
Reputation: 1270
I suggest you look into how React useState
and useEffect
work.
The effect didn't run yet
const [data, setData] = useState({}) // This declares the state
// This effect is not run until the component has already rendered.
useEffect(() => {
axios(`https://api.openweathermap.org/data/2.5/forecast?q=Istanbul&appid=c681e6e33ec339728fdf88e0b24a2a01`)
.then(res => console.log(res.data))
.catch(err=> console.log(err))
}, [])
// Pass in an empty array, otherwise there is an infinite loop.
// Comment if you don't understand why
// Since the effect didn't run yet, the first time around data will be undefined (or rather {} which is the default you set in the `useState`).
const { city, list } = data
console.log(city.name)
// This means, you can't access `data.city.name`, since `data` doesn't even have a `city` property (which is to say `data.city` is undefined, explaining the error message)
This means the first time your function is called (the first render), the data has not loaded yet.
Even after you load it, you haven't updated the state:
const [data, setData] = useState({})
useEffect(() => {
axios(`https://api.openweathermap.org/data/2.5/forecast?q=Istanbul&appid=c681e6e33ec339728fdf88e0b24a2a01`)
.then(res => console.log(res.data)) // You're not calling setState
.catch(err=> console.log(err))
// Since you didn't call setState, all the function does is log it to the console after the render
})
// Even the second time around, since state was not updated, it remains `{}`
const { city, list } = data
console.log(city.name)
const [data, setData] = useState({})
useEffect(() => {
axios(`https://api.openweathermap.org/data/2.5/forecast?q=Istanbul&appid=c681e6e33ec339728fdf88e0b24a2a01`)
.then(res => setState(res.data)) // setState
.catch(err=> console.log(err))
})
const { city, list } = data
console.log(city?.name) // Should log `undefined` first, then the real result
EDIT: The solution logs undefined
first because the first time the component runs, it hasn't fetched the data yet. The order goes:
useEffect
called after the render. This loads the datauseEffect
changes the state via setData
Upvotes: 0
Reputation: 30739
Since API calls are asynchronous, you need to wait for the response before you assign the value to the state. Thus, you need to move the code inside the then
block. Then it will work:
function ApiCall({
getCities
}) {
const [data, setData] = useState({})
useEffect(() => {
axios(`https://api.openweathermap.org/data/2.5/forecast?q=Istanbul&appid=c681e6e33ec339728fdf88e0b24a2a01`)
.then(res => {
const data = res.data;
const {
city,
list
} = data
console.log(city.name)
})
.catch(err => console.log(err))
}, [])
Also add an empty array []
to your useEffect
hook if you want to load the API on page load.
Upvotes: 1