103

How to pass extra parameters to an onClick event using the ES6 syntax?

For instance:

handleRemove = (e) => {

}

render() {
     <button onClick={this.handleRemove}></button>
}

I want to pass an id to the handleRemove function like this:

<button onClick={this.handleRemove(id)}></button>
Cheng
  • 16,824
  • 23
  • 74
  • 104

9 Answers9

176

Remember that in onClick={ ... }, the ... is a JavaScript expression. So

... onClick={this.handleRemove(id)}

is the same as

var val = this.handleRemove(id);
... onClick={val}

In other words, you call this.handleRemove(id) immediately, and pass that value to onClick, which isn't what you want.

Instead, you want to create a new function with one of the arguments already prefilled; essentially, you want the following:

var newFn = function() {
  var args = Array.prototype.slice.call(arguments);

  // args[0] contains the event object
  this.handleRemove.apply(this, [id].concat(args));
}
... onClick={newFn}

There is a way to express this in ES5 JavaScript: Function.prototype.bind.

... onClick={this.handleRemove.bind(this, id)}

If you use React.createClass, React automatically binds this for you on instance methods, and it may complain unless you change it to this.handleRemove.bind(null, id).

You can also simply define the function inline; this is made shorter with arrow functions if your environment or transpiler supports them:

... onClick={() => this.handleRemove(id)}

If you need access to the event, you can just pass it along:

... onClick={(evt) => this.handleRemove(id, evt)}
Philippe Sultan
  • 2,111
  • 17
  • 23
Michelle Tilley
  • 157,729
  • 40
  • 374
  • 311
  • how to use event.preventDefault() along with this.handleRemove(id,etv) inside onClick function in this way(es6). – Tushant Apr 23 '16 at 10:53
  • 47
    worth mentioning that arrow function would cause a re-render every time, since new function is created on each render – Restuta Aug 14 '16 at 21:22
  • Someone should definitely write about this "gotcha" (perhaps a blog post)? I would write about this, but I'm learning ReactJS (and advanced Javascript), but this tip is vitally important. So, as newbie, I did `this._deleteText(result.text)`, and when my table loaded, each table row with a delete button fired off delete calls... ouch! – user1322092 Dec 03 '16 at 22:54
  • 2
    Something to note, adding the arrow function in the render method will create a new function on every new render. It will work, though. It's better to create the new bound function in the constructor and add the new function to the render method. – databyss Feb 01 '17 at 22:42
55

Use the value attribute of the button element to pass the id, as

<button onClick={this.handleRemove} value={id}>Remove</button>

and then in handleRemove, read the value from event as:

handleRemove(event) {
...
 remove(event.target.value);
...
}

This way you avoid creating a new function (when compared to using an arrow function) every time this component is re-rendered.

Praym
  • 2,038
  • 23
  • 21
  • 5
    Nice and easy. Had an issue when the button had a icon/glyph in it though with the value being undefined. Just use `event.currentTarget.value` instead of `event.target.value` and all is good. – Rory Sep 06 '17 at 11:05
  • Glad it helped you. In my example, I assumed that the – Praym Sep 07 '17 at 11:33
  • 1
    I find it cleaner to use a simple function here for event handling. Both the approaches of binding or creating a function that returns another function will create lot of garbage as a new function will be created and old one discarded to be garbage collected on each render . – Praym Sep 07 '17 at 11:40
  • I get `Uncaught TypeError: Cannot read property 'handleRemove' of undefined`. And this is even after doing `this.handleRemove = this.handleRemove.bind(this);` at the top of my `constructor(props)`. – vapcguy Mar 15 '18 at 22:22
34

Use Arrow function like this:

<button onClick={()=>{this.handleRemove(id)}}></button>
Fantasy Shao
  • 615
  • 4
  • 8
  • 9
    This creates a new function on every render which is not great for performance. ESLint flags it by default: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md – craigpatik Jan 30 '17 at 21:28
  • 1
    @craigpatik so if you were to create a named function as a property initializer or via .bind in the constructor and use that in place of the anonymous arrow function as shown above it would prevent creating a new function on every render.. and you could still curry your 2nd **this.handleRemove(id)** function inside of it? – Jax Cavalera Feb 09 '17 at 18:18
  • 2
    Linting error: `JSX functions should not use arrow functions react/jsx-no-bind` – vapcguy Mar 15 '18 at 22:14
18
onClick={this.handleRemove.bind(this, id)}

Using with arrow function

onClick={()=>{this.handleRemove(id)}}
elpddev
  • 4,314
  • 4
  • 26
  • 47
NK Chaudhary
  • 401
  • 5
  • 13
  • 1
    Not sure why someone downvoted this. It worked for me. Is it considered bad form to do it this way? – Cari Mar 23 '17 at 12:22
  • No Cari...its perfect you can use this. – NK Chaudhary Mar 28 '17 at 07:37
  • 4
    This is consider inefficient please read https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md – xiao May 23 '17 at 19:31
  • 3
    This is basically the same as the accepted answer. And it's not efficient since it's creating a new function on each render. You should bind your functions in the constructor, not in the render method. So, works but may have a high cost in performance. – mayid Nov 23 '17 at 14:48
  • Linting error: `JSX props should not use .bind() react/jsx-no-bind` – vapcguy Mar 15 '18 at 22:16
11

Something nobody has mentioned so far is to make handleRemove return a function.

You can do something like:

handleRemove = id => event => {
  // Do stuff with id and event
}

// render...
  return <button onClick={this.handleRemove(id)} />

However all of these solutions have the downside of creating a new function on each render. Better to create a new component for Button which gets passed the id and the handleRemove separately.

Redmega
  • 673
  • 5
  • 21
  • I'm not sure why this is upvoted, 3 times! This does not bind the this.handleRemove function to the onClick event, this executes it immediately when the component renders, then bind whatever it returns to the onClick event. – Giovanni Lobitos Jan 29 '18 at 09:58
  • @GiovanniLobitos In the end you get a function which has access to `id` and `event` and performs the duties of `handleRemove()`. Not sure what the issue you have with it is? Did you face any problems implementing? "Whatever it returns" is the aforementioned function with access to `id` and `event`. – Redmega Jan 29 '18 at 20:45
  • I see. Got confused right there. Thanks for the clarification. – Giovanni Lobitos Jan 30 '18 at 01:07
4

TL;DR:

Don't bind function (nor use arrow functions) inside render method. See official recommendations.

https://reactjs.org/docs/faq-functions.html


So, there's an accepted answer and a couple more that points the same. And also there are some comments preventing people from using bind within the render method, and also avoiding arrow functions there for the same reason (those functions will be created once again and again on each render). But there's no example, so I'm writing one.

Basically, you have to bind your functions in the constructor.

class Actions extends Component {

    static propTypes = {
        entity_id: PropTypes.number,
        contact_id: PropTypes.number,
        onReplace: PropTypes.func.isRequired,
        onTransfer: PropTypes.func.isRequired
    }

    constructor() {
        super();
        this.onReplace = this.onReplace.bind(this);
        this.onTransfer = this.onTransfer.bind(this);
    }

    onReplace() {
        this.props.onReplace(this.props.entity_id, this.props.contact_id);
    }

    onTransfer() {
        this.props.onTransfer(this.props.entity_id, this.props.contact_id);
    }

    render() {
        return (
            <div className="actions">
                <button className="btn btn-circle btn-icon-only btn-default"
                    onClick={this.onReplace}
                    title="Replace">
                        <i className="fa fa-refresh"></i>
                </button>
                <button className="btn btn-circle btn-icon-only btn-default"
                    onClick={this.onTransfer}
                    title="Transfer">
                    <i className="fa fa-share"></i>
                </button>                                 
            </div>
        )
    }
}

export default Actions

Key lines are:

constructor

this.onReplace = this.onReplace.bind(this);

method

onReplace() {
    this.props.onReplace(this.props.entity_id, this.props.contact_id);
}

render

onClick={this.onReplace}
mayid
  • 1,644
  • 15
  • 23
  • `'PropTypes' is not defined`. You're missing something. – vapcguy Mar 15 '18 at 22:42
  • 1
    Of course @vapcguy. You have to import it: `import PropTypes from 'prop-types'` – mayid Mar 16 '18 at 15:17
  • Excellent answer - would be curious to see how it works with this.setState instead of props. I'm missing something on my end to close the loop. – serraosays Jan 16 '19 at 17:13
  • and what if those functions are defined as constant variables? – akkonrad Jul 30 '19 at 14:50
  • @akkonrad, it would be the same. Fact is, there's a new function created on heap each time the render method is executed. However, notice that this changed now that we have react hooks, and declaring functions at render time is common practice, but only with hooks as far as I can tell, because react team take care of the performance in that case. – mayid Jul 31 '19 at 15:14
4

in function component, this works great - a new React user since 2020 :)

handleRemove = (e, id) => {
    //removeById(id);
}

return(<button onClick={(e)=> handleRemove(e, id)}></button> )
Robin Jiao
  • 113
  • 10
2

I use the following code:

<Button onClick={this.onSubmit} id={item.key} value={shop.ethereum}>
    Approve
</Button>

Then inside the method:

onSubmit = async event => {
    event.preventDefault();
    event.persist();
    console.log("Param passed => Eth addrs: ", event.target.value)
    console.log("Param passed => id: ", event.target.id)
    ...
}

As a result:

Param passed in event => Eth addrs: 0x4D86c35fdC080Ce449E89C6BC058E6cc4a4D49A6

Param passed in event => id: Mlz4OTBSwcgPLBzVZ7BQbwVjGip1

Riccardo Persiani
  • 702
  • 1
  • 5
  • 25
0

I am using React-Bootstrap. The onSelect trigger for dropdowns were not allowing me to pass data. Just the event. So remember you can just set any values as attributes and pick them up from the function using javascript. Picking up those attributes you set in that event target.

    let currentTarget = event.target;
    let currentId = currentTarget.getAttribute('data-id');
    let currentValue = currentTarget.getAttribute('data-value');
Dlaugh14
  • 313
  • 1
  • 5
  • 16