4

I have a component that renders tags. It loops through a Map and displays the data. I tryed using forEach but it doesn't work. However it works if I convert the map to an array (foreach doesn't work on the array either). What am I missing here?

This works:

render(){
    return(
        <div class="container">
            {Array.from(this.props.tags.values()).map((tag,i) => (
                <Tag
                    handleKeyDown={this.handleKeyDown.bind(this)}
                    handleBlur={this.handleBlur.bind(this)}
                    handleTouchTap={this.handleTouchTap.bind(this)}
                    handleRequestDelete={this.handleRequestDelete.bind(this)}
                    tag={tag}
                    key={i}
                />
            ))}
        </div>
    )
}

This doesn't:

render(){
    return(
        <div class="container">
            {this.props.tags.forEach((tag,i) => (
                <Tag
                    handleKeyDown={this.handleKeyDown.bind(this)}
                    handleBlur={this.handleBlur.bind(this)}
                    handleTouchTap={this.handleTouchTap.bind(this)}
                    handleRequestDelete={this.handleRequestDelete.bind(this)}
                    tag={tag}
                    key={i}
                />
            ))}
        </div>
    )
}

1 Answer 1

1

Map#forEach doesn't return a new array. They both work exactly as you'd expect, which is for Array#map to build a new array mapped from the old one. React.createElement needs to take its children as an argument list, or as an array. Generally you want to think of your Map more like a plain Object than as an array, i.e. you convert it to an array if you want to manage its values separately.

Your Array.from use is a fine approach. This is how I'll typically use Maps. If you wanted to get really modern with it, and avoid one of the iterations (though I can only imagine that mattering at all for the most extreme of cases), you could always apply an iterator function to your Map's values, then spread on that iterator.

render() {
  const asTags = function* () {
    for (const [key, tag] of this.props.tags) {
      yield <Tag tag={tag} key={key} />;
    }
  };

  return (
    <div class="container">
      {[...asTags()]}
    </div>
  );
}

The generator function is a simple iterator, which yields each of the entries it loops through in your Map. I only use the array in there because I'm not totally sure how to spread arguments inside a JSX (one reason I don't use JSX). If you had React.createElement imported as ce, you could simply say ce('div', { class: 'container' }, ...asTags()) in the render instead.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! Is there a better way of doing this other than converting the map to an array and useing map on that?
Sure @Gopid, I've updated the answer with an example using generators, which saves you having to iterate once to convert to an array, and lets you directly iterate and map the entries to your new format.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.