loading...

React — Access custom params in handlers the right way

flexdinesh profile image Dinesh Pandiyan Updated on ・2 min read

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! ⚡️

Posted on Jun 6 '19 by:

flexdinesh profile

Dinesh Pandiyan

@flexdinesh

Engineer | Speaker | Blogger | OSS | I build things ☕

Discussion

markdown guide
 

I don't think that I would like to place data that I already have in the DOM and then retrieving it again from the DOM, but there could definitely be some use cases for this approach. Thanks for sharing!

 

The example given isn't relevant. If the id is coming from the props object, one can simply read it from through the closure in which case passing the data as an id to the html element and reading it from the event is useless.

If performance is a concern, one can also create a new component, pass the the necessary data as a prop and define the callback in the child.
I guess the described method could be useful for optimisation when dealing with third-party components though.

 

The example was to give an idea that values can be stored as data attributes. I'll update the example to access state values so it's obvious.

 

I like it. Seems like a simple, down and dirty way to avoid using anonymous functions when there isn't a better answer. There are plenty of places where this would have been handy. Thanks!

 

So simple - but perfectly effective. Nice!

 

This is a really cool idea! I am currently using anonymous arrow functions in my components/apps, maybe I'll refactor and use this approach.

 

it's just awesome
thx a lot, man