Ohas
Ohas

Reputation: 1871

Efficiently adding click listener to an arbitrary number of elements

I want to listen for click events on an arbitrary number of elements and inside the click event handler, I want to retrieve some info about the clicked element (info which was easily accessible during element creation).

A common solution to this problem is this:

render() {
  return (
    <div>
      {this.props.users.map(user => (
        <button onClick={() => this.buttonClicked(user.email)}>
          {user.name}
        </button>
      ))}
    </div>
  );
}

The flaw I see in this approach is that we're creating a new function for every element. Is that a problem worth solving? If it is, how do you feel about this solution:

render() {
  return (
    <div>
      {this.props.users.map((user, index) => (
        <button data-index={index} onClick={this.buttonClicked}>
          {user.name}
        </button>
      ))}
    </div>
  );
}

buttonClicked(event) {
  const { index } = event.currentTarget.dataset;
  const { email } = this.props.users[index];
  // ...
}

Upvotes: 2

Views: 61

Answers (2)

Cleiton
Cleiton

Reputation: 18113

Create another component and dispatch the event from it.

class Button extends React.PureComponent{
  handleOnClick = ()=>{
    const {onClick, ...rest} = this.props
    if(onClick typeof ==='function'){
      onClick(rest)
    }
  }
  render(){
    const {name} = this.props
    return (
      <button onClick={this.handleOnClick}>
        {name}
      </button>)
  }
}

...

render() {
  return (
    <div>
      {this.props.users.map(user => (
        <Button {...user} key={user.email} onClick={this.buttonClicked} />
      ))}
    </div>
  );
}

Upvotes: 1

Daniel Apt
Daniel Apt

Reputation: 2638

I believe it is better to use a defined function instead of anonymous/inline functions, otherwise the onClick handler only gets created during the render stage.

In your second example, you can actually bind the argument to the handler:

render() {
  return (
    <div>
      {this.props.users.map((user, index) => (
        <button data-index={index} onClick={this.buttonClicked.bind(this, user.email)}>
          {user.name}
        </button>
      ))}
    </div>
  );
}

Secondly, wanted to point out that there is no performance issue with having many handlers. React uses one event handler at the root of your document.

Upvotes: 0

Related Questions