Good day : )
I’m very confused about the following React code.
I get data from an API successfully.
I define a state property “foodName” and asign my API data to this property.
Now I can set additional state properties to subproperties of this foodName property:
this.setState({ measure: this.state.foodName.hints[0].measures[0].label });
^This works as expected. I can render this state to the DOM and see my data.
BUT I can’t instead render my original API data (the returned data, dataJSON, that was assigned to the foodName property) to the DOM via:
{this.state.foodName.hints[0].measures[0].label}
^this throws an error of “Cannot read property ‘0’ of undefined”
Please explain if you can see what is going on. This is very confusing to me.
Thanks in advance
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
foodName: {},
test: "state is posting successfully"
};
this.getData = this.getData.bind(this);
}
componentDidMount() {
this.getData();
}
async getData() {
let url =
"https://api.edamam.com/api/food-database/parser?nutrition-type=logging&ingr=red%20apple&app_id=e9e86788&app_key=2c231e68f3531783f0fed14057834b04";
let response = await fetch(url);
//console.log(response);
let dataJSON = await response.json();
console.log(dataJSON.hints[0].measures[0].label);
this.setState({ foodName: dataJSON });
//console.log(this.state.foodName.hints[0].measures[0].label);
this.setState({ measure: this.state.foodName.hints[0].measures[0].label });
}
render() {
return (
<div>
<div>{this.state.test}</div>
<div>{this.state.measure}</div> {/* This returns a value */}
<div>{this.state.foodName.hints[0].measures[0].label}</div>
{/* This doesn't return a value - error "Cannot read property '0' of undefined
App.render */}
</div>
);
}
}
Hello, have you try logging foodName to the console to see if its defined ?
hi @Gabehaus
I see that your getting JSON response from your API, why you are using two this.setStates, you could do just one, like this:
const data = {
foodName: dataJSON,
measure : dataJSON.hints[0].measures[0].label
};
this.setState(data);
Logging foodName returns something like “cannot log object” as foodName should be an object
Yes, but that doesnt’ solve my problem
exactly, so the values are not in foodName which explain why you are getting “cannot read property 0 of undefined”. console.log(typeof foodName). Lets see what type it is. From there we should know where to go and what is wrong.
typeof this.state.foodName = object
It will really help to know what’s in the object. Any way i can get access to your code base ??
I don’t see how this works:
this.setState({ measure: this.state.foodName.hints[0].measures[0].label });
but this doesn’t render:
render()
return
<div>{this.state.foodName.hints[0].measures[0].label} </div>
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
foodName: {},
test: "state is posting successfully"
};
this.getData = this.getData.bind(this);
}
componentDidMount() {
this.getData();
}
async getData() {
let url =
"https://api.edamam.com/api/food-database/parser?nutrition-type=logging&ingr=red%20apple&app_id=e9e86788&app_key=2c231e68f3531783f0fed14057834b04";
let response = await fetch(url);
//console.log(response);
let dataJSON = await response.json();
console.log(dataJSON.hints[0].measures[0].label);
this.setState({ foodName: dataJSON });
//console.log(this.state.foodName.hints[0].measures[0].label);
this.setState({ measure: this.state.foodName.hints[0].measures[0].label });
//console.log(this.state.foodName + "pebo");
console.log(typeof this.state.foodName);
}
render() {
return (
<div>
<div>{this.state.test}</div>
<div>{this.state.measure}</div> {/* This returns a value */}{" "}
{/* <div>{this.state.foodName.hints[0].measures[0].label}</div>This doesn't return a value - error "Cannot read property '0' of undefined
App.render */}
</div>
);
}
}
I am able to go through your code now. It wasn’t working because what you were doing what wasn’t logical in react. You were trying to access nested objects or object in the dom which isn’t proper by react. All your accessing should be done in your logic like how you reassigned the part you need to another state “measure” or destructure the part you need to different states on mounting life cycle. Why you cant access objects in the dom is definitely because reacts assumes you want to render the property in the objects which is rather console fit for testing purposes rather than the dom (probably because of the complexity of keys and value pairs) if you think about it. Arrays (single or nested arrays that doesn’t involve object) can be used in the dom instead because it is simple for react to know you want to display it contents. if objects is involved in arrays then you will want to use array methods like filter or map to tell react dom what to do with e.g display it as ordered list or unordered. I hope this make sense. 
@Gabehaus You are calling getData() function in componentDidMount(). Actually how the Component Lifecycle works in react is, first the component is created (constructor called) and then it is mounted (for this purpose render() is called) and after that componentDidMount() is called. So your code is failing in the first render. This is because in the first render foodName is an empty object (since you initialised it to an empty object). So foodName.hints is undefined and an error occurs here. In subsequent renders however, after the data has been loaded, foodName.hints is defined and react successfully renders the component.
So to solve this problem, you can create an attribute loading in this.state and set it to true. Then once the data is available, you can update state and set loading to false. Also in render(), you would have to conditionally render either loading indicator or data depending upon value of this.state.loading.
Refer to below code for a correct implementation :
import React, { Component } from "react";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
foodName: {},
loading: true
};
this.getData = this.getData.bind(this);
}
componentDidMount() {
this.getData();
}
async getData() {
let url =
"https://api.edamam.com/api/food-database/parser?nutrition-type=logging&ingr=red%20apple&app_id=e9e86788&app_key=2c231e68f3531783f0fed14057834b04";
let response = await fetch(url);
let dataJSON = await response.json();
this.setState({ foodName: dataJSON, loading: false });
}
render() {
return this.state.loading ? (
<h1>Laoding...</h1>
) : (
<div>{this.state.foodName.hints[0].measures[0].label}</div>
);
}
}