Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

JavaScript

Brandon Benefield
Brandon Benefield
7,739 Points

SOLVED: React + Axios: Getting a Cannot read property error

I am creating a simple weather app using the OpenWeatherMAP API. I'm getting an error when trying to access a property that is deeper than the first level of the list array in the JSON. I can console.log() the property and it returns the correct value, but when trying to use it as a prop is when I experience the error. Now I can render the values of anything inside list as long as it's not another object. So id and name are safe. But going into main or coord for example will return undefined when trying to render the value but will return the correct value if I console.log() it.

JSON

{
  message: "accurate",
  cod: "200",
  count: 1,
  list: [
    {
        id: 4166776,
        name: "Ocoee",
        coord: {},
        main: {
          temp: 304.15,
          pressure: 1010,
          humidity: 58,
          temp_min: 304.15,
          temp_max: 304.15
        },
    }
  ]
}

index.js

import React, { Component } from 'react';
import { render } from 'react-dom';
import axios from 'axios';

import Header from './modules/Header';
import Input from './modules/Input';
import Weather from './modules/Weather';

import './style.css';

class App extends Component {
  constructor() {
    super();
    this.state = {
      weather: []
    };
  }

  getWeather = query => {
    axios.get(`https://api.openweathermap.org/data/2.5/find?q=Ocoee&appid=<API KEY>`)
      .then(response => {
        this.setState({
          weather: response.data.list[0]
        });
        console.log(response.data.list[0]); //returns JSON correctly
        console.log(this.state.weather.main.temp); //returns correct value (304.15)
      })
      .catch(error => {
        console.log('Error', error);
      });
  };

  queryWeather = (event) => {
    if (event.key === 'Enter') {
      event.preventDefault();

      this.getWeather();
    }
  }

  render() {
    return (
      <div className='content'>
        <Header />
        <Input queryWeather={this.queryWeather} />
        <Weather 
           city={this.state.weather.name}
           temp={this.state.weather.main.temp} //this is where the problem is
         />
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

Weather.js

import React from 'react';
import PropTypes from 'prop-types';

const Weather = props =>
  <div>
    <p>{props.city}</p> //returns correct city name
    <p>{props.temp}</p> //should return temperature but is undefined
  </div>

export default Weather;

SOLVED:

Just in case someone stumbles upon this, I have this solved. It may not be the best solution but it works. All I did was add some more empty arrays to my state object and populate them with values from the JSON.

index.js

  this.state = {
    weather: [],
    temp: [], //gets info from response.data.list[0].main.temp
    clouds: [] //gets info from response.data.list[0].weather[0].description
  };
}

  getWeather = query => {
    axios.get(`https://api.openweathermap.org/data/2.5/find?q=${query}&units=imperial&appid=f92c1f4990b0574d4a4e4d3dd556f388`)
      .then(response => {
        this.setState({
          weather: response.data.list[0],
          temp: response.data.list[0].main.temp, //pushes data to this.state.temp
          clouds: response.data.list[0].weather[0].description //pushes data to this.state.clouds
        });
      })
      .catch(error => {
        console.log('Error', error);
      });
  };
Anders Blom
Anders Blom
9,124 Points

Another way of doing this (and for scalability) is to pass the entire response.data.list[0] object to <Weather /> as props (<Weather weatherData={this.state.weather} />, and access the keys of that object with this.props.weatherData.main.temp for example.

In your index.js render() method you can also check to see if the this.state.weather-object is undefined or not, and render based on this fact. This should stop the error, and even give you a chance to create a loader/spinner/display errors if the API is unavailable, and whatnot:

render() {
  if (this.state.weather === undefined) {
    return (<div>Fetching weather, please wait..</div>)
  } else {
    return (<Weather weatherData={this.state.weather} />)
  }
}

Of course, if you want to take this further, React 16 introduced Error Boundaries, so this can be done in an even better way that assures that only "parts" of your app will throw errors and not discard the entire app because of a small error in a child-component somewhere. You can read more about Error Boundaries here: https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html