1

I am mapping over an array of objects that looks like this

events = [
   {
     id: 1,
     name: 'Real vs Barcelona'
   },
   {
     id: 2,
     name: 'Celtic vs Rangers'
   }
];

I then want to pass the id to a function that updates the relevant event. This work, I am using an arrow function to achieve this.

onClick={() => this.props.updateEvent(event.id)}

render() {

    return (
      <div>
         {this.props.events.map(event => {
           return <p key={event.id} onClick={() => this.props.updateEvent(event.id)}>{ event.name }</p>
         });
      </div> 
    )
}

I have read there are performance implications of doing it this way, and also my es-linter is showing an error. Does anyone know another way I can approach this and pass the id in a different way.I don't think I want to use the ES5 bind approach either.

Maybe something like this

render() {

let updateEvent = (e) => {
  //This obviously won't work but something similar? 
  this.props.updateEvent(e.target);
}

return (
  <div>
     {this.props.events.map(event => {
       return <p key={event.id} onClick={updateEvent}>{ event.name }</p>
     });
  </div> 
)

}

2
  • If you don't want to use arrow function you could do onClick={function() { this.props.updateEvent(event.id) } } Commented Sep 28, 2017 at 15:16
  • It only matters if it is a performance issue.. It will create a new function on every render for each item.. You could on ComponentWillReceiveProps create a map of functions and store it on the component, and then in the loop say this.eventFuncs[event.id] but I have never seen it as a perf issue.. Commented Sep 28, 2017 at 15:28

3 Answers 3

4

If the value you want to pass is serializable then you can assign it as an attribute as Chris mentions in his answer.

The more generic solution is to create a new component and pass the data as props:

class Event extends React.Component {
  _onClick = () => this.props.onClick(this.props.event.id);

  render() {
    return (
      <p onClick={this._onClick}>{ this.props.event.name }</p>
    );
  }
}

And in the caller:

<div>
   {this.props.events.map(event => {
     return <Event key={event.id} event={event} onClick={this.props.updateEvent} />
   });
</div> 
Sign up to request clarification or add additional context in comments.

4 Comments

Ah, you beat me to it. Was gonna add this option to my post ;)
There is a runnable example of something similar to the above that I made not long ago, here: stackoverflow.com/a/46192328/2030321
@FelixKling thanks for the answer. I can accept yours or Chris' but what I was thinking of was something like my answer below. Can you comment on the approach in that answer, and whether it is better/the same/worse than other solutions
@RaulRodriguez your approach will have the same performance impact which you were talking about in your initial approach, as it will recreate the function in every render, although the performance issue is very minor. Personally, I would go with Felix's approach, as Chris's solution is a bit difficult to extend, plus you have to add a data-* attribute to make it work.
0

Since events pass the related DOM element you clicked on, you could read any attribute the element has. As such, you could do something similar to what you suggested at the end of your post:

class MyApp extends React.Component {

  render() {
    let updateEvent = (e) => {
      console.log(e.target.dataset.name);
    }
    
    return (
      <div>
         {[{id:0, name:"foo"}, {id:1, name:"bar"}].map(event => {
           return <p key={event.id} data-name={event.name} onClick={updateEvent}>{ event.name }</p>
         })}
      </div> 
    );
  }
}
 
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

5 Comments

I don't think e.target.name will work. <p> elements do not have such a property. You probably want to use a data-* attribute.
Please don't use this approach. It will do what you ask, but you will have a hard time extending your "event" object, since you will need to update the DOM also.
@TryingToImprove, what do you mean by "extending the event"?
@Chris thanks for the answer. I can accept yours or Felix's but what I was thinking of was something like my answer below. Can you comment on the approach in that answer, and whether it is better/the same/worse than other solutions
@Chris I am thinking about adding new values/properties to the object. Then it needs to update the DOM also..
0

I actually figured out what I think I was looking for.

render() {   

    return (
      <div>
         {this.props.events.map(event => {

            let updateEventHelper = () => {
               this.props.updateEvent(event.id);
            };

           return <p key={event.id} onClick={updateEventHelper}>{ event.name }</p>
         });
      </div> 
    )
}

4 Comments

How does it get the event.id? :S
@TryingToImprove apologies, mistake when writing out the code here, updated now
@RaulRodriguez, no this is not good. For simple uses (just an id) you can use either, for more complex situations use Felix's approach.
This is exactly the same as your original code. Whether you assign the function to a variable first or not doesn't make a difference.

Your Answer

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