50

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>
    )
};
matanster
  • 15,072
  • 19
  • 88
  • 167
Swaraj Ghosh
  • 2,284
  • 3
  • 16
  • 19

6 Answers6

55
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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Henrik Andersson
  • 45,354
  • 16
  • 98
  • 92
  • This throws a warning in console. "You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call" – crush Oct 21 '15 at 18:09
  • 7
    ^^ just `.bind(null, attributeId)` – wesbos Nov 28 '15 at 01:16
  • 2
    That would also run `bind` on every re-render... which is sub-optimal, to say the least. – ZenMaster Jul 26 '16 at 08:12
  • @ZenMaster you can always `var boundedViewMore = viewMore.bind(null, attributeId);` `onClick={this.boundedViewMore}` – George Oct 05 '16 at 13:57
  • 5
    @George Still would run `bind` on every `render` call. Wasteful. The most optimal way, IMO, is to do it in constructor: `this.onclick = this.handleClick.bind(this);` Then, in `handleClick`, retrieve `attributeId` from the React event: `const {attributeId} = event.target`. – ZenMaster Oct 05 '16 at 15:11
  • 4
    Here is a [CodePen](http://codepen.io/anon/pen/rrpwaw?editors=1011) illustrating the approach. – ZenMaster Oct 05 '16 at 15:18
  • those are two separate lines, with the first executed on the same level as the render function. – George Oct 05 '16 at 20:25
  • 3
    Zen is correct - the pollution of the render method is not necessary if you use a constructor within the component. While this answer works, and might even be in some doc examples, the constructor will suit you better if you would like to maximize efficiency. The largest problem with most react apps is the render method containing too much weight, or in other cases firing unchecked. – Lux.Capacitor Oct 20 '16 at 19:24
  • 1
    @Lux.Capacitor @Zen It's an almost 2 year old answer and I totally agree with current practices. However when answer was posted the OP was using `React.createClass()` which really didn't have any constructor. – Henrik Andersson Oct 21 '16 at 11:31
  • @Lux.Capacitor I'm a bit confused here, how would the constructor approach avoid binding the event function on every `render`. Perhaps someone can point me at some relevant, contemporary-version example? e.g. for this [related question](https://stackoverflow.com/questions/49863427/react-js-event-binding-in-render). The CodePen by @ZenMaster still binds the function inside of `render`. – matanster Apr 16 '18 at 17:57
  • 2
    @matanster as Zen describes in Note #1 section here - https://stackoverflow.com/a/39878181/1530023 - putting .bind in the render method causes a new function declaration every time you render that component. This behavior can in turn cause child components to also re-render if they are using that as a prop or something similar. For what it's worth also, JS has to hoist that function on each creation, so there can be latency if you render a component like this as a list item hundreds of times with multiple children inside it too. – Lux.Capacitor Apr 16 '18 at 21:42
  • @matanster Also, you seem to be confusing two separate "bindings". The one you seem to refer to is `onClick={this.handleClick}` which translates to an event handler being attached to the DOM element (parent, in many cases). The binding we were talking about is the one that happens here: `onClick={this.onClick.bind(this)}` which binds `onClick` method to a context. – ZenMaster Apr 22 '18 at 19:47
  • @HenrikAndersson As the answer is top rated here, can you please update with optimal solution so that others won't be confused. There are two solutions 1. Using the arrow function as it will bind automatically when there is no need to pass params, or bind it in constructor so that it can stop unnecessery rerender – zakir Feb 19 '19 at 09:45
  • @zakir please edit the answer if you think it's prudent to update it and i'll have a look, thanks! – Henrik Andersson Feb 19 '19 at 09:47
  • @HenrikAndersson By Currying I had updated the solution, Please have a look . – zakir Feb 19 '19 at 11:39
40

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');
  };
};
ZenMaster
  • 12,363
  • 5
  • 36
  • 59
  • This is by far the best answer, not only is it a more organized usage, but it keeps the render cycle from pollution - not to mention needless verbosity. – Lux.Capacitor Oct 20 '16 at 19:18
  • I used a similar approach but using HTML5 data-* attributes. The handler function just did an e.target.attributes['data-XXXX'].value. – Josef.B Feb 08 '17 at 01:04
  • `e.target.dataset.XXXX` would be better, if that is your approach. In any case -- the above (in the answer) is React's abstraction over that part. – ZenMaster Feb 08 '17 at 07:10
  • I think we mix html with JSX very often. Ok, you avoid a bind per render (it should render only on state change) but you are adding an attribute to the JSX object (this is not html). I seriously doubt about performance been way better than the loss on readability and complexity. – pikilon Aug 08 '17 at 08:41
  • 1
    @pikilon "Adding attribute to JSX object"? What does that mean? I am creating a method, a bound one, on a JS class that extends React component. Of course it is preferable to re-binding on each render (although with TurboFan in V8 it should be much much better). That aside -- it is much more idiomatic this way. – ZenMaster Aug 08 '17 at 20:20
  • I mean when you write this `

    ` you are really writting this: `React.createElement( "h3", { id: this.props.id, onClick: this.onClick }, this.props.name)` triggering to render in the real dom the html code. You have to think also in the transpiled code I did this example (goo.gl/MYPoFe) to illustrate the huge code difference. **is there some stats with the benefits?**

    – pikilon Aug 09 '17 at 06:40
  • @pikilon I am a big believer in explicit semantic code. I like the levels of both in my example. I don't think any readability was sacrificed on the altar of performance. Much like defining a function in a loop ... – ZenMaster Aug 09 '17 at 11:06
  • Isn't there a way to not bind the event on each and every render? – matanster Apr 16 '18 at 17:47
  • 1
    @matanster You mean the passing of `onClick` as a prop? Well, I am not sure. The actual binding, the DOM and stuff, happens after you return from `render`. Whether React rebinds the event every time, I have no idea. Why would that be a concern? – ZenMaster Apr 19 '18 at 20:43
  • @ZenMaster well, it doesn't make sense to rebind an event on every render of the DOM element. Think about e.g. animation. It's enough to bind it once, especially if react knows how to only update the coordinates rather than remove and re-insert the DOM element! – matanster Apr 22 '18 at 08:26
  • @matanster I would imagine that binding an event is part of a diffing mechanism and eventual reconciliation. So, if on a specific render pass, the event binding isn't changed (the function passed to the `onClick` is the same reference), it probably won't be a part of a patch React creates as a result of that diff, and thus -- won't be rebound in the __actual DOM__. As such, it should not have any "real" impact on the animation, aside from the time it took to compute the diff and such, which has to be done anyway. – ZenMaster Apr 22 '18 at 19:41
  • @matanster Also, see my comment to OP on two types of "bindings" we have here. – ZenMaster Apr 22 '18 at 19:48
  • This 'answer' is a mess (as of NOTE 1 -- Apr 14, 2018), can someone clean it up & give only ONE answer & explanation please? – tomByrer Feb 09 '19 at 04:57
  • I think ZenMaster's answer is far more better than the accepted one. – perfectionist1 Feb 24 '21 at 14:12
  • How do we use this if it is a hook ? Since hooksdo not allow use of "this" – Shreehari Feb 24 '22 at 14:00
15

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}
Community
  • 1
  • 1
aikeru
  • 3,773
  • 3
  • 33
  • 48
  • 4
    But doesn't this have the negative side effect of creating a new function object on each render call? – Aaron_H Jun 28 '16 at 07:45
  • 1
    @Aaron_H it does, but so do other solutions like ```.bind```. As I see it, it's 1) one of the recommended approaches in the docs ( https://facebook.github.io/react/docs/reusable-components.html ), 2) a micro-optimization to worry about the function creation at least in the React projects I've worked on. If you want to avoid that, you'll need to bind or assign arrow functions perhaps in the constructor of the component and pass the functions themselves in the jsx... – aikeru Jun 28 '16 at 14:46
  • 1
    Or use properties from Babel's stage-2 which is cleaner still. – Dave Newton Nov 12 '16 at 22:37
7

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")}
Sagiv b.g
  • 30,379
  • 9
  • 68
  • 99
  • In this way the method is called at every render, not only when you click it – Apperside Jun 28 '19 at 13:35
  • @Apperside true. Though in most cases this is not a problem (if you referring to performance) – Sagiv b.g Jun 28 '19 at 13:43
  • Actually I am referring to the fact that if I put something inside an onClick listener, maybe I want it to be executed when I click it. That doesn't have any sense – Apperside Jun 29 '19 at 08:16
  • @Apperside there are 2 methods here, the outer one which you invoke with a parameter and the second method which get returned as the actual event listener. The trick here is that the second method is closing over the parameter which will allow you to do with it whatever you want. The event handler will get invoked only when event is triggered (click in this case) and not on each render as you mentioned. – Sagiv b.g Jun 29 '19 at 14:17
  • I see your point, it actually makes sense, I still wonder how such approach could impact on memory since it created a new function at every render... – Apperside Jun 30 '19 at 09:23
  • @Apperside you may find your answer in the [docs](https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render) – Sagiv b.g Jun 30 '19 at 09:41
3

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

roshan
  • 2,410
  • 2
  • 25
  • 37
0

Here is the code how to use on click event.


var attributeId = "groups_";
  attributeId = 32;

  const viewMore = (val) => () => {
    console.log(val)
  }
  return (
    //parent div
    <div className="groups" id={attributeId} onClick={viewMore(attributeId)}>
      <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>
  )

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 21 '22 at 14:57