44
votes

I would like to pass the parent div id, on click of that div or any child element of the same div. But I am unable to achieve it. Please tell me where I am making a mistake. Code is below:

viewMore: function(i,j){
        console.log('You clicked: ', i  );
    },

render : function(){
  var attributeId = "groups_";
  attributeId+= index;
  return(
  //parent div
    <div className="groups" id={attributeId} onClick={this.viewMore}>
        <div className="floatLeft"> Group Name: <h3>My Name</h3></div>
            <span className="floatRight typeCd">POC</span>
        <div className="clearfix"> Key Attributes: 
            <ul>
                <li> POC 1</li>
            </ul>
        </div>
    </div>
    )
};
5

5 Answers

53
votes
viewMore = (i,j) => () => {
    console.log(i,j)
}

To pass parameters to event handlers we need to use currying. With the above method no new functions created all the time while render is called.

40
votes

Since I see these kind of suggestions in multiple places, I am going to move my comment into an answer as well, to provide an additional view:

class TestComponent extends React.Component {
  constructor() {
    super();
    this.onClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    const {id} = event.target;
    console.log(id);
  }

  render() {
    return (
      <div>
        <h3 id={this.props.id} onClick={this.onClick}>
          {this.props.name}
        </h3>
      </div>
    );
  }
}

This allows to:

  1. avoid unnecessary binds
  2. access the id and whatever else properties in a much more React-ive manner.

Of course, the above example assumes that you receive the id as a prop, but you can do the necessary manipulations as well.

UPDATE 1 -- Nov 28, 2016

Added link to CodePen from comments above.

UPDATE 2 -- Mar 30, 2017

As mentioned, this wouldn't work if you use React.createClass to define your components. You don't have a constructor to pull this off. You can use other lifecycle methods, if you don't mind a little ugliness.

Having said that, it is 2017. Use ES6, would you?!

UPDATE 3 -- May 12, 2017

If you are using class properties transform, then you can simplify it further:

class TestComponent extends React.Component {
  onClick = (event) => {
    const {id} = event.target;
    console.log(id);
  }

  render() {
    return (
      <div>
        <h3 id={this.props.id} onClick={this.onClick}>
          {this.props.name}
        </h3>
      </div>
    );
  }
}

UPDATE 4 -- Feb 4, 2018

Due to improvements of bind and friends in V8 (Chakra and such probably too), you just may be better off using the this.click.bind(this) or wrapping it in an arrow function when passing to onClick.

Why?

The previous method, created for performance reasons only, closed some possibilities for dynamically injecting functions onto the component's prototype.

NOTE 1 -- Apr 14, 2018

Keep in mind that the method mentioned in Update 4 still introduces some performance issues, as on each render pass a new function is created as a result of bind. This, in turn, will trickle down to the child component and cause unnecessary re-renders, as the function changes each time.

The same thing happens when you pass an arrow function inline.

All other methods, like using class properties, will mess with your inheritance (which you should be avoiding, but still), simply due to the fact that, currently, Babel transpiles them to "on-instance" functions, which are not on the prototype chain.

So, this:

class Person {
  printA = () => { console.log('a') }
}

becomes:

function _classCallCheck(instance, Constructor) {...abridged...}

var Person = function Person() {
  _classCallCheck(this, Person);

  this.printA = function () {
    console.log('a');
  };
};
15
votes

I've made an updated answer for ES6 here: https://stackoverflow.com/a/35748912/76840

Essentially, you can use arrow function expressions, which have the benefit of preserving this:

onClick={(event)=>this.viewMore(attributeId, event)}

As of this edit, if you're using Babel with stage-2 enabled, you can use a property like so:

// Within your class...
viewMore = (event) => { /* ... */ }
// Within render method in your JSX
onClick = {this.viewMore}
7
votes

You can use currying function.

ES5:

viewMore(param) { // param is the argument you passed to the function
    return function(e) { // e is the event object that returned

    };
}

ES6

viewMore = param => e => {
  // param is the argument you passed to the function
  // e is the event object that returned
};

And just use it like this:

onClick={this.viewMore("some param")}
2
votes

Here is an update and an overview of previous answers:

  1. Using onClick={this.viewMore.bind(this, attributeId)} by @HenrikAndersson .While this approach serves the purpose it uses the bind syntax with which many are not comfortable.
  2. Using public class field mentioned by @ZenMaster.This solution has more or less the same performance, it also comes with a better syntax. But it turns tricky when we have to pass a parameter.

    class TestComponent extends React.Component {
      onClick = (event) => {
        const {id} = event.target;
        console.log(id);
      }
    
      render() {
        return (
          <div>
            <h3 id={this.props.id} onClick={this.onClick}>
              {this.props.name}
            </h3>
          </div>
        );
      }
    }
    

The above mentioned approach skips passing parameters and instead uses custom attributes to access the data required in click handler.

A better solution would be :

class MyComponent extends React.Component {

    handleClick = (item) => (e) => {
        e.preventDefault()    
        console.log(`This has access to item ${item}! and event(e)`)
    }

    render(){
        const item={ id:'1', value: 'a' }
        return(
            <button onClick={ this.handleClick(item) }  >Click</button>
        )
    }

}

Reference: Handle events by arrow functions in React app