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>
    )
};

6 Answers 6

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.

Sign up to request clarification or add additional context in comments.

15 Comments

^^ just .bind(null, attributeId)
That would also run bind on every re-render... which is sub-optimal, to say the least.
@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.
Here is a CodePen illustrating the approach.
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.
|
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');
  };
};

15 Comments

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.
I used a similar approach but using HTML5 data-* attributes. The handler function just did an e.target.attributes['data-XXXX'].value.
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.
@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.
@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?
|
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}

3 Comments

But doesn't this have the negative side effect of creating a new function object on each render call?
@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 ( 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...
Or use properties from Babel's stage-2 which is cleaner still.
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")}

6 Comments

In this way the method is called at every render, not only when you click it
@Apperside true. Though in most cases this is not a problem (if you referring to performance)
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 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.
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...
|
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

Comments

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>
  )

1 Comment

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.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.