Iván Herrera
Iván Herrera

Reputation: 37

React - Unhandled Rejection (TypeError): Cannot read property 'city' of undefined

I'm currently developing a website where Team information is retrieved here:

export default class TeamInfo extends React.Component{
    
    constructor(props) {
        super(props);
     
        this.state = {
          isShow: true,
          team: []
        };
        this.getTeam();
      }    

    getTeam(){
        const axios = require("axios");
        const team_id = this.props.id;
        axios.get(API+'/team/'+ team_id).then(res => {
            this.setState({team : res.data})
            console.log('inside teaminfo... ' + this.state.team.location.city);
        })
    }
 render() {
        return(
         <div><h1>{this.state.team.location.city}</h1></div>
)}
}

This is the structure of the team JSON answer:

{
    "name": "Barcelona",
    "shield": "shield.png",
    "location": {
        "city": "Barcelona",
        "country": "SPAIN"
    },
    "score": 74626,
}

I'm trying to access team location as this.state.team.location.city , when I log it in console is shown correctly but Unhandled Rejection (TypeError): Cannot read property 'city' of undefined is shown in the website.

Any hint or help will be greatly appreciated.

Upvotes: 0

Views: 790

Answers (3)

Malaba Eric
Malaba Eric

Reputation: 26

You can as well use the new ES2020 chain operator to check if a property is present inside an object like this:

  render() {
    return (
  {this.state.team.map(team => (
    {team?.location ?<div><h1>{team.location.city}</h1></div>: null}
  ))}
  );        
  }

The Chain operator ?. will return undefined if location is not found inside team, it returns the object otherwise.

Upvotes: 0

Eric
Eric

Reputation: 1357

Your team data is initialized in your constructor as below

this.state = {
  isShow: true,
  team: []
};

This caused the error during the first render, as .team.location.city is undefined. On the second render, it is good after you setState with a new value.

To fix this, you need to check if the value is undefined or set the initial value for location.city in the constructor.

render() {
        return(
         <div><h1>{typeof this.state.team.location !== "undefined"  && typeof this.state.team.location.city !== "undefined" && this.state.team.location.city}</h1></div>
)}

Upvotes: 1

Drew Reese
Drew Reese

Reputation: 202836

Given the component code, your state.team is an array, so you would need to access it using array indices.

this.state.team[0].location.city

OFC, this assumes the array is populated, so use a guard check first to ensure the first element exists.

this.state.team[0] && this.state.team[0].location.city

You can also conditionally render it as well

export default class TeamInfo extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isShow: true,
      team: []
    };
    this.getTeam();
  }

  getTeam() {
    const axios = require("axios");
    const team_id = this.props.id;
    axios.get(API + "/team/" + team_id).then(res => {
      this.setState({ team: res.data });
    });
  }
  render() {
    return this.state.team[0] ? (
      <div>
        <h1>{this.state.team[0].location.city}</h1>
      </div>
    ) : null;
  }
}

And also since it is an array, mapping the result is also a common pattern

export default class TeamInfo extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isShow: true,
      team: []
    };
    this.getTeam();
  }

  getTeam() {
    const axios = require("axios");
    const team_id = this.props.id;
    axios.get(API + "/team/" + team_id).then(res => {
      this.setState({ team: res.data });
    });
  }
  render() {
    return (
      {this.state.team.map(team => (
        <div>
          <h1>{team.location.city}</h1>
        </div>
      ))}
    );
  }
}

Note:

this.setState({team : res.data})
console.log('inside teaminfo... ' + this.state.team.location.city);

State updates are "asynchronous", the update happens between render cycles, so the console log will only ever log the current state from this render cycle. Log the updated state in a lifecycle function like componentDidUpdate.

Upvotes: 1

Related Questions