JBd
JBd

Reputation: 115

React 16 iterating over map rendering key

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>

Upvotes: 1

Views: 969

Answers (1)

Patrick Hund
Patrick Hund

Reputation: 20236

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

Upvotes: 2

Related Questions