0

This is based on exercise 2.14 of this course https://fullstackopen.com/en/part2/getting_data_from_server.

The user can select a country, then the weather information for that country's capital will be dislpayed. My code gives me error Cannot read property 'temperature' of undefined

const Weather = ({ city }) => {
const [weatherDetails, setWeatherDetails] = useState([])

useEffect(() => {
    axios.get('http://api.weatherstack.com/current', {
        params: {
            access_key: process.env.REACT_APP_WEATHER_KEY,
            query: city
        }
    }).then(
        (response) => {
            setWeatherDetails(response.data)
        }
    )
}, [city])
console.log('weather', weatherDetails);

return (
    <div>
        <h3>Weather in {city} </h3>
        {weatherDetails.current.temperature}
    </div>
)}

Basically, the line

{weatherDetails.current.temperature}

makes my code crash. When I do remove that line, I am able to see the response thanks to the console.log, but there are two consecutive logs

weather []
weather {request: {…}, location: {…}, current: {…}}

I figured that my code happens in between these two, and it tries to access the data before it has even arrived, but I don't know what to do to fix this.

Also, I don't know what the argument [city] of useEffect() does, so it'd be great if someone can explain to me what it does.

Edit: Solved! Set weatherDetail's initial state to null and did some conditional rendering

if (weatherDetails) {
    return (
        <div>
            <h3>Weather in {capital}</h3>
            {weatherDetails.current.temperature} Celsius
        </div>
    )
} else {
    return (
        <div>
            Loading Weather...
        </div>
    )
}
0

1 Answer 1

5

weatherDetails is an empty array, initially, so there is no current property to read from.

Use some conditional rendering. Use initial null state and then check that it is truthy to access the rest of the object when it is updated.

const Weather = ({ city }) => {
const [weatherDetails, setWeatherDetails] = useState(null) // <-- use null initial state

useEffect(() => {
    axios.get('http://api.weatherstack.com/current', {
        params: {
            access_key: process.env.REACT_APP_WEATHER_KEY,
            query: city
        }
    }).then(
        (response) => {
            setWeatherDetails(response.data)
        }
    )
}, [city])
console.log('weather', weatherDetails);

return (
    <div>
        <h3>Weather in {capital} </h3>
        {weatherDetails && weatherDetails.current.temperature} // check that weatherDetails exists before accessing properties.
    </div>
)}

What does the argument [city] of useEffect do?

This is the hook's dependency array. Hooks run on each render cycle, and if any values in the dependency array have updated it triggers the hook's callback, in this case, the effect to get weather data when the city prop updates.

useEffect

By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.