Reputation: 179
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
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
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
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