razvanusc
razvanusc

Reputation: 179

"TypeError: Cannot read property 'name' of undefined" error

The code gives me this "TypeError: Cannot read property 'name' of undefined" error although when I console.log it it gives me the object

import React from "react";

class Random extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      breweries: []
    };
  }

  componentDidMount() {
    fetch("https://api.openbrewerydb.org/breweries")
      .then(response => response.json())
      .then((data) => {
        this.setState({
          breweries: data,
        })
      })
  }

  render() {
    const brewery = this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)];
    return(
      <div>{brewery.name}</div>
    )
  }
}

export default Random;

Upvotes: 0

Views: 2750

Answers (3)

Bens Steves
Bens Steves

Reputation: 2849

There are two approaches to this issue.

Approach 1: UNSAFE_componentWillMount()

You could use UNSAFE_componentWillMount() instead of componentDidMount(). UNSAFE_componentWillMount() executes before the component is rendered so you will have the data in the state before the content is rendered. This should get rid of the error.

Approach 2: Verifying the State is !Empty

In your render method, you can add an if statement that says breweries is null if there is no data in state else breweries equals this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)].

Something like this:

render() {
    const { breweries } = this.state;
    let brewery; 
    if (breweries.length === 0){
       brewery = null;
    } else {
      brewery= 
   this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)];
    }
    return(
      <div>{brewery.name}</div>
    )
  }

For extra insurance, you can combine the two approaches. If you did that your component would look like this:

import React from "react";

class Random extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      breweries: []
    };
  }

  componentWillMount() {
    fetch("https://api.openbrewerydb.org/breweries")
      .then(response =>{ return response.json() })
      .then((data) => {
        this.setState({
          breweries: data,
        })
      })
  }

  render() {
    const { breweries } = this.state;
        let brewery; 
        if (breweries.length === 0){
           brewery = null;
        } else {
          brewery= 
       this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)];
        }
    return(
      <div>{brewery.name}</div>
    )
  }
}

export default Random;

That should do it. Hope this helps.

Edit: WARNING

I wrote willMount() but it should be UNSAFE_componentWillMount(). componentWillMount() will not work after version 17. UNSAFE_componentWillMount() will work after version 17. Sorry about that. I forgot to write unsafe.

Upvotes: 1

Sam Denty
Sam Denty

Reputation: 4085

You should ensure you've fetched the data, before attempting to access an index of the array. Your random math will evaluate to 0 when the array is empty, resulting in undefined

import React from "react";

class Random extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      breweries: null
    };
  }

  componentDidMount() {
    fetch("https://api.openbrewerydb.org/breweries")
      .then(response => response.json())
      .then((data) => {
        this.setState({
          breweries: data,
        })
      })
  }

  render() {
    if (this.state.breweries) {
      const brewery = this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)];
      return(
        <div>{brewery.name}</div>
      )
    }

    return null
  }
}

export default Random;

Upvotes: 0

kind user
kind user

Reputation: 41893

Until the async request finishes,

this.state.breweries[Math.floor(Math.random()*this.state.breweries.length)] 

will actually return undefined, since

Math.floor(Math.random()*this.state.breweries.length)

will return 0, and since this.state.breweries is an empty array, it will return undefined

([][0] -> undefined).


You have to secure the possibility that brewery is yet undefined, e.g.:

<div>{brewery && brewery.name}</div>

Upvotes: 2

Related Questions