How to pass a parameter to event handlers in React
By Flavio Copes
Learn how to pass a parameter to an onClick event handler in React by wrapping the call in an arrow function, so it does not run immediately on mount.
To pass a parameter to an event handler in React, wrap the call in an arrow function: onClick={() => removeBill(index)}. Let’s see why the obvious way doesn’t work.
When you work on a React function component you might have the need to attach an event to onClick (or other events).
You usually do:
<button onClick={addBill}>Add</button>
This works because you’re passing a reference to the function. React stores it and calls it when the click happens.
But what if you have to pass a parameter? Say you have a list of bills, and you want to remove one by clicking the “X” next to it.
You can’t do:
<button onClick={removeBill(index)}>𝗫</button>
because adding the parentheses means you’re calling the function right there, while rendering. onClick receives the return value of removeBill(), not the function itself. This is going to delete all the bills in the list, as soon as the app is started.
It gets worse if removeBill() updates state. Removing a bill re-renders the component, the render calls removeBill() again, and React stops you with:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
Instead, this is what you need to do, using arrow functions:
<button onClick={() => removeBill(index)}>𝗫</button>
Now onClick receives a function again. When the user clicks, React calls that arrow function, which in turn calls removeBill(index).
A complete example
Here’s the pattern inside a list rendered with map():
function BillsList({ bills, removeBill }) {
return (
<ul>
{bills.map((bill, index) => (
<li key={bill.id}>
{bill.description}
<button onClick={() => removeBill(index)}>𝗫</button>
</li>
))}
</ul>
)
}
Each button gets its own arrow function that remembers its own index.
What if I also need the event object?
React passes the event to your handler. With the arrow function wrapper, accept it and forward it:
<button onClick={(event) => removeBill(index, event)}>𝗫</button>
This is handy when you need event.stopPropagation(), for example when the button sits inside a clickable row.
One last thing. The arrow function is recreated on every render. In a normal app this costs nothing, so don’t worry about it. It only matters if you’re passing the handler to a component optimized with React.memo, and even then, measure before optimizing.