TL;DR - You can access render time values in handlers without having to use anonymous arrow functions. Hint — leverage data attributes.
React is great in so many aspects and it gives us the freedom to do things in different ways by being less opinionated (or non opinionated).
In recent days, especially after the release of hooks, the community has been fussing around a lot on reference equality and how anonymous arrow functions in renders are not good for performance.
We are not going to deep dive into why or how using arrow functions during render affects the performance (or it does not). Here are two contrasting tweets for context.
When
We mostly use arrow functions during render only to pass custom parameters to event handlers.
For example, this is a common use case —
const MyComp = () => {
const handleClick = (e, id) => {
// handle click and use id to update state
}
// id comes from state
return (
<div onClick={(e) => handleClick(e, id)}>Hello World!</div>
)
}
How
Instead of using an anonymous arrow function here, we could leverage data attributes and access values from the event object.
const MyComp = () => {
const handleClick = (e) => {
const id = e.target.dataset.id
// handle click and use id to update state
}
// id comes from state
return (
<div data-id={id} onClick={handleClick}>Hello World!</div>
)
}
Advantages
This approach has a lot of advantages —
- You can memoize the callback pretty easily if needed.
const MyComp = () => {
// id comes from state
const handleClick = React.useCallback((e) => {
const id = e.target.dataset.id
// handle click and use id to update state
}, [id])
return (
<div data-id={id} onClick={handleClick}>Hello World!</div>
)
}
You don't create a new function during component render which saves additional computation cost during vdom diffing.
If child components that use React.memo take this handler as a prop, you don't have to write custom areEqual method to circumvent referential integrity issues with anon arrow functions.
Keep in mind that all data attributes return String values even if you pass in other primitive types. So it's a good practice to coerce your value while extracting from dataset.
const id = e.target.dataset.id // this will be String
const id = Number(e.target.dataset.id) // convert to type if needed
I have created a codesandbox to demonstrate how it works — Custom Params In Handlers
You are amazing! Have a great day! ⚡️
So simple - but perfectly effective. Nice!