Shaoz
Shaoz

Reputation: 10653

In Reactjs, when doing nested loop nothing is rendered

I have a props which includes users array and messages array. Each user and message is related through a corresponding ID.

I want to match these IDs so I can render messages with the user who posted it.

The issue is nothing is rendering on screen but in the console.log(), I can see the results. Please, can someone shed some light into this.

Also I'm not sure if I'm doing this the right way, so if you can show me a better way, I'll be glad.

Thanks

React:

import React, { Component } from 'react';

class Messages extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    let self = this;

    return (
      <ul>
      {
        this.props.messages.map((message, msg_key) => {
          self.props.members.forEach((member) => {
            if (member.id === message.userId) {
              console.log('inside foreach message.userId', message.userId); // this shows in console.
              console.log('inside foreach member.id', member.id); // this shows in console.
              return <p>member.id</p> // nothing shows on screen.
            }
          })
        })
      }
      </ul>
    );
  }
}

export default Messages;

Upvotes: 1

Views: 147

Answers (3)

John Ruddell
John Ruddell

Reputation: 25862

you should redefine your data structure. What you currently have is not going to be performant.

instead of an array of members.. you should have an object key'd by the member id. This is so you can lookup a member by their id. Anytime you have information that is going to be used for other fields of data you should have a key value storage setup key'd by the unique identifier that maps to the other pieces of information. Heres an example

members: {
    1: { id: 1, name: 'foo'},
    2: { id: 2, name: 'bar'}
}

when you define a data structure like this you can easily look up members for messages.

render (){
    const elems = [];
    this.props.messages.forEach((message, msg_key) => {
        const member = this.props.members[message.userId];
        if (member) {
            elems.push(
                <li key={msg_key}>
                    <p>{message.title}</p> /* or whatever you want from the message */
                    <p>{member.id}</p>
                </li>
            );
        }
    })
    return (
      <ul>
          {elems}
      </ul>
    )
}

Upvotes: 1

Tyler Sebastian
Tyler Sebastian

Reputation: 9458

forEach has no side-effect. Use map

return (
  <ul>
  {
    this.props.messages.map((message, msg_key) => {
      return self.props.members.map((member) => { // <-- this line
        if (member.id === message.userId) {
          console.log('inside foreach message.userId', message.userId); // this shows in console.
          console.log('inside foreach member.id', member.id); // this shows in console.
          return <p>member.id</p> // nothing shows on screen.
        }
      })
    })
  }
  </ul>
)

what I mean by "side-effect" is that it neither returns anything nor modifies the original array - map returns a modified array.

remember to supply a key prop when returning an array.

edit

Ah, actually ^ doesn't do what you want.

Array has a nice method to accomplish what you're attempting, find

return (
  <ul>
  {
    this.props.messages.map((message, msg_key) => {
      const member = self.props.members.find((member) => member.id === message.userId)
      return <p>{member.id}</p>
    })
  }
  </ul>
)

edit #2

well... it actually does work, but it's not pretty. React won't render null in arrays, so it'll skip the ones where the condition wasn't true...

Upvotes: 1

feng_nyc
feng_nyc

Reputation: 157

I think you might messed up some of the attributes inside the array by having 2 loops at 1 time (map, and forEach).

Try have a helper function to map through (instead of forEach) the member.id attributes. So just call {this.renderMemberList} inside the render().

Do you have a JSFiddle set up? it would be easier

Upvotes: 0

Related Questions