Reputation: 933
My React Component has the following render
componentWillMount () {
var url = 'https://gist.githubusercontent.com/hart88/198f29ec5114a3ec3460/raw'
Request.get(url)
.then(data => {
this.setState({cakes: data.text});
})
}
render() {
return(
<div>
{this.state.cakes} //prints this ok
{
this.state.cakes.map(cake =>{ // error here
return <p>{cake.title}</p>;
})
}
</div>
);
}
i am trying to loop through this.state.cakes which is an array of objects.
What am i doing wrong here ?
Update - an abbreviated example of this.state.cakes
:
[
{
"title": "Lemon cheesecake",
"desc": "A cheesecake made of lemon",
"image":"https://s3-eu-west-1.amazonaws.com/s3.mediafileserver.co.uk/carnation/WebFiles/RecipeImages/lemoncheesecake_lg.jpg"
},
{
"title":"Banana cake",
"desc":"Donkey kongs favourite",
"image":"http://ukcdn.ar-cdn.com/recipes/xlarge/ff22df7f-dbcd-4a09-81f7-9c1d8395d936.jpg"
}
]
Thanks
Upvotes: 0
Views: 925
Reputation: 8414
I believe that you've used curly braces (understandably) where React actually requires parentheses. Since you're getting the data from a fetch
, be sure to set your constructor with a preliminary cakes
object as well. Try this:
constructor(props) {
super(props)
this.state = {
cakes: []
}
}
render() {
if (this.state.cakes.length > 0){
return(
<div>
{
this.state.cakes.map(cake => (
return <p>{cake.title}</p>;
))
}
</div>
);
}
return null
}
The issue is that the component is rendering and you're telling it to do something with an array called this.state.cakes
, but this.state.cakes
hasn't been defined yet because the fetch
hasn't returned yet. Setting your constructor like this passes an empty array to the render
so it doesn't freak out, and then when your data loads and your state updates, it will re-render with your data.
The reason {this.state.cakes}
was, on its own, rendering just fine is because for the first split second of the component's existence, that value was undefined
, which means that React basically just ignored it - once the data loaded, it rendered. However, the map
method failed because you cannot pass an undefined array into map
.
And as Ha Ja suggested, you should probably add a key
attribute to the <p>
elements.
Upvotes: 1
Reputation: 63524
If the state is set as the resutl of a fetch you might not be able to access the data immediately due to the async operation. You can catch this by inspecting the state and if it has no length return a message or a spinner component to indicate the data's on its way.
Once state.cakes
is updated with the data from the fetch operation the component will re-render.
constructor(props) {
super(props);
this.state = { cakes: [] };
}
componentDidMount() {
fetch('/cakes')
.then(res => res.json())
.then(cakes => this.setState({ cakes }));
}
render() {
if (!this.state.cakes.length) return <Spinner />
return (
<div>
{this.state.cakes.map(cake => {
return <p>{cake.title}</p>;
})};
</div>
)
}
As the others have mentioned it's also good practice to add key
s to your iterated elements.
Upvotes: 1
Reputation: 1826
Here:
{this.state.cakes.map((cake, i) => <p key={i}>{cake.title}</p>;)}
Do not forget to add the key
attribute.
Ps: It would be better to use an unique Id instead of the array index. SO if you have an id for each array item, better write:
{this.state.cakes.map(cake => <p key={cake.id}>{cake.title}</p>;)}
Upvotes: 1
Reputation: 2090
You missed brackets inside of your map
{this.state.cakes.map(cake =>{ // errors here
return <p> {cake.title} </p>;
})}
Upvotes: 0