Spdexter
Spdexter

Reputation: 933

Loop through objects React

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

Answers (4)

skwidbreth
skwidbreth

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

Andy
Andy

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 keys to your iterated elements.

Upvotes: 1

hjrshng
hjrshng

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

Christopher Messer
Christopher Messer

Reputation: 2090

You missed brackets inside of your map

    {this.state.cakes.map(cake =>{ // errors here
        return <p> {cake.title} </p>;
    })}

Upvotes: 0

Related Questions