React - passing a value with onClick

I ran into an issue where I had a list of items being rendered and wanted to trigger an action if one of them was clicked.

For instance consider this component that renders a list of items. It creates an li for each item. How would you handle a click from each li and include the associated item.

render: function() {  
  let items = this.props.items;
  return (
    <ul>
    {() => {
      items.map((item) => {
        return <li key={item.id}>{item.title}</li>
      });
    }()};
    </ul>
  );
},

onItemClick: function() {  
  // how to I get which item was clicked???
}

The question is how to retrieve the underlying value that was in scope when the li was created?

The first and natural solution is to create a component for each item in the list and handle the events inside the sub-component. Most examples demonstrate this. It's very much the React way.

However, it's not always feasible to do this and is sometimes overkill. This is where bind comes into play.

Using bind, you can prepend arguments to the argument list when that method is called. To do this, in your iterator, you will bind the event handler to the current component and include the item in the bind operation. This will include the item when the handler is fired!

It will look something like this:

render: function() {  
  let items = this.props.items;
  return (
    <ul>
    {() => {
      items.map((item) => {
        // bind the components onItemClick method
        // and use the bind syntax that prepends 
        // arguments to attach the item argument
        let boundItemClick = this.onItemClick.bind(this, item);

        // Construct the onClick with our bound function
        return <li key={item.id} onClick={boundItemClick}>{item.title}</li>
      });
    }()};
    </ul>
  );
},

onItemClick: function(item, e) {  
  console.log(item);
}

You can use this same approach in any event handler where you want to explicitly pass a value. It doesn't have to be just inside an iterator.

render: function() {  
  let boundClick = this.clickHandler.bind(this, 'Hello');
  return (
    <div onClick={boundClick}>Click me</div>
  );
},

clickHandler: function(text, e) {  
  console.log(text);
}

The example above binds the clickHandler to prepend the argument 'Hello' when it gets called.

That's all there is to it!

comments powered by Disqus