0
votes

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;
3
when you console log what brewery.name or just brewery?Bens Steves
when i console log only breweryrazvanusc
do you get this in your console: [ ] then [...your data] ? so do two things log to the console?Bens Steves
no, I just get the objectrazvanusc

3 Answers

2
votes

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>
1
votes

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.

0
votes

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;