1

I'm trying to get some weather data from an API, but I always get the same error of not being able to read properties of undefined. I've gone through different tutorials and previously asked issues, but I haven't been able to figure out what I'm doing wrong. Could anyone please give me a hand?

export default function Weather(){
  const apiKey = process.env.REACT_APP_API_KEY
  const weatherUrl = `http://api.weatherapi.com/v1/current.json?key=${apiKey}&q=Saxthorpe&aqi=no`
  const [weatherData, setWeatherData] = useState();
  const [error, setError] = useState(null);
  
  useEffect(() => {

    (
      async function(){
        try {
          const response = await axios.get(weatherUrl);
          setWeatherData(response.weatherData);
        } catch (error) {
          setError(error);
        } 
      }
    )(); 
  }, [])
  return (
    <div className="weather-feature">
      <h1>hi</h1>
      <p className="location">{weatherData.location.name}</p>
      <p className="temp">{weatherData.current.temp_c}</p>
      <p className="weather-desc">{weatherData.current.condition.text}</p>
    
    </div>
  )
}


1
  • const [weatherData, setWeatherData] = useState(); You've set your initial state to undefined, so when you try to access weatherData.location, you get an error. Modify your code to check for undefined, and either render null or render a loading placeholder. Commented May 29, 2022 at 16:47

3 Answers 3

7

When pulling data like this and rendering components conditional on that data, you should account for situations in which the data is not yet available or null.

Specifically, you're attempting to render this data:

  return (
    <div className="weather-feature">
      <h1>hi</h1>
      <p className="location">{weatherData.location.name}</p>
      <p className="temp">{weatherData.current.temp_c}</p>
      <p className="weather-desc">{weatherData.current.condition.text}</p>
    
    </div>

But it's not going to available on the first render (i.e. weatherData does not have a location property at first, since your default useState value is undefined).

There are many ways around this, and what you choose ultimately depends on your project and preferences.

You can use optional chaining as a simple protection against null references when checking nested properties:

  return (
    <div className="weather-feature">
      <h1>hi</h1>
      <p className="location">{weatherData.location?.name}</p>
      <p className="temp">{weatherData.current?.temp_c}</p>
      <p className="weather-desc">{weatherData.current?.condition?.text}</p>
    
    </div>

Or you can return something else if weatherData is not ready. A good tool for this kind of thing is swr:

import useSWR from 'swr'

function Weather()
{
  const { weatherData, error } = useSWR(weatherUrl, fetcher)

  if (error) return <div>failed to load</div>
  if (!weatherData) return <div>loading...</div>
  return <div>hello {weatherData.location}!</div>
}

As a side note, another thing to consider is your useEffect dependencies:

  useEffect(() => {

    (
      async function(){
        try {
          const response = await axios.get(weatherUrl);
          setWeatherData(response.weatherData);
        } catch (error) {
          setError(error);
        } 
      }
    )(); 
  }, [])

With an empty dependency array, your effect runs only on mount and unmount. If you want it to run based on some other variable(s) changing, add those variables to the dependency array.

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

Comments

0

Look: weatherData is your state, which is initially... nothing, because you don't pass any data.

So, you cannot access the location field on the first render because it does not exist yet.

It would help if you made sure weatherData exist:

return (
    <div className="weather-feature">
      <h1>hi</h1>
      <p className="location">{weatherData?.location.name}</p>
      <p className="temp">{weatherData?.current.temp_c}</p>
      <p className="weather-desc">{weatherData?.current.condition.text}</p>
    </div>
  )

Comments

-1

You can debug to check the response. I think the respose is undefined from

const response = await axios.get(weatherUrl);

response = undefined => can not get weatherData property.

We are using useEffect you can debug on it by F12 in Chrome and see what happen and the reason of this bug. This is better than you come here to ask

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.