1

I am trying to iterate over an Immutable.js map, to render a component, however although this is rendering, it is also rendering the key to the page. I am not sure why.

render() {
    const myMap = new Map({foo: '', bar: 'http://google.com/'})
    const {showFoo, titleAndUrl} = this.props
    return (
      <ul style={[
        styles.container,
        showFoo && styles.container.inline,
      ]}>
        {myMap.map((title, url) => this.renderTab(url, title))}
      </ul>
    )
  }

  renderTab(title, url) {
    const {showFoo, isFoo} = this.props
    return (
      <li key="sb" style={[
        styles.listItem,
        showFoo && styles.listItem.inline,
      ]}>
        <a
          href={url}
          key={title}
          style={[
            styles.link,
            styles.activeLink,
            showFoo && styles.link.inline,
          ]}
          className={isFoo ? "style" : ''}>
          {title}
        </a>
      </li>
    )
  }
}

The two names and urls are rendered correctly, however duplicate keys are rendered i.e. foo is rendered twice and so is bar, but one of the foo and bar keys has no styles, which suggests it's being rendered outside of this.renderTab

See image: enter image description here

Rendered HTML:

<ul data-radium="true"
    style="display: flex; align-items: center; padding: 0px; margin: 0px; list-style: none; width: 100%; border-top: 1px solid rgb(221, 221, 221); height: 48px;">
    foo
    <li data-radium="true" style="display: flex; width: 50%; height: 47px; cursor: default;"><a href=""
                                                                                                class=""
                                                                                                data-radium="true"
                                                                                                style="display: flex; justify-content: center; align-items: center; width: 100%; text-decoration: none; font-weight: 500; font-size: 16px; color: rgb(0, 31, 91); transition: color 0.1s linear;">foo</a>
    </li>
    bar
    <li data-radium="true" style="display: flex; width: 50%; height: 47px; cursor: default;"><a
            href="http://google.com" class="" data-radium="true"
            style="display: flex; justify-content: center; align-items: center; width: 100%; text-decoration: none; font-weight: 500; font-size: 16px; color: rgb(0, 31, 91); transition: color 0.1s linear;">bar</a>
    </li>
</ul>
JBd
  • 115
  • 2
  • 9
  • `map()` returns a list, it doesn’t only loop over elements. This might be related for some reason.But I am not sure why it would return the key not the whole component. – Y2H Nov 22 '17 at 09:44

1 Answers1

2

You have mixed up the order of your arguments, assigning title to url and vice versa.

Also, the arguments passed to the callback function for Immutable.Map.map are (1) value, (2) key, so the fist argument is your URL and the second is your title.

Change the line with the call to map like so:

{myMap.map((url, title) => this.renderTab(title, url))}

Another problem is that the list item elements you are rendering all have the same key “sb”, only the key of the “a” elements change, but that is not even needed.

Change the JSX returned by renderTab to this:

  <li key={title} style={[
    styles.listItem,
    showFoo && styles.listItem.inline,
  ]}>
    <a
      href={url}
      style={[
        styles.link,
        styles.activeLink,
        showFoo && styles.link.inline,
      ]}
      className={isFoo ? "style" : ''}>
      {title}
    </a>
  </li>

Finally, the main mistake is that you expect Immutable.Map.map to return an array, but it doesn't, it returns another immutable map, so you have to convert the value returned by the map function to an array using valueSeq and toArray.

So your map statement should actually look like this:

{myMap.map((url, title) => this.renderTab(title, url)).valueSeq().toArray()}

see related post on Stack Overflow

Patrick Hund
  • 19,163
  • 11
  • 66
  • 95