Wrap in an Arrow When You Need Extra Data
Passing Arguments to Handlers
How to pass extra information (like an item id) into an event handler without breaking everything.
What you'll learn
- Pass arguments to handlers
- Avoid the "calling on render" pitfall
A handler usually needs the event object — (e) => …. But sometimes
you also want extra data: which item was clicked, which row to
delete. There are two clean ways.
1. Arrow Wrapper
Wrap your function in an inline arrow and pass the args you want:
function List({ items, onDelete }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => onDelete(item.id)}>x</button>
</li>
))}
</ul>
);
} The arrow creates a brand-new function on every render — usually
fine in lists. (If a render-performance audit later flags it, you’ll
use useCallback.)
2. Curry a Handler Factory
When you call the same pattern many times, factor out a maker:
function List({ items, onDelete }) {
const makeDelete = id => () => onDelete(id);
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={makeDelete(item.id)}>x</button>
</li>
))}
</ul>
);
} Same outcome, slightly more explicit shape.
Keeping the Event Object Too
If you need BOTH extra args and the event, take both in the arrow:
<button onClick={e => handleClick(e, item.id)}>x</button> The Anti-Pattern To Avoid
// ✗ Calls onDelete(item.id) on every render — fires immediately, infinite loop if it sets state
<button onClick={onDelete(item.id)}>x</button>
// ✓ Wrap in an arrow so it fires only on click
<button onClick={() => onDelete(item.id)}>x</button> Up Next
You’ve reacted to clicks. Now React needs to remember things —
useState.