13

I am using inline arrow function to change the onClick handlers of some divs in my React component, but I know it is not a good way in terms of performance.

Objectively, what is the most efficient way of setting onClick handlers that require arguments? This is what I have tried:

1. Inline arrow function

changeRoute (routeName) {
  console.log(routeName)
}
render() {
  return (
    <>
      <div onClick={() => this.changeRoute("page1")}>1</div>
      <div onClick={() => this.changeRoute("page2")}>2</div>
    </>
  )
}

2. If I use constructor binding then how can I pass props?

constructor() {
  super(props)
  this.changeRoute = this.changeRoute.bind(this)
}
changeRoute (routeName) {
  console.log(routeName)
}
render() {
  return (
    <>
      <div onClick={this.changeRoute}>1</div>
      <div onClick={this.changeRoute}>2</div>
    </>
  )
}

3. If I remove the arrow function then the function being called on the render itself

changeRoute (routeName) {
  console.log(routeName)
}
render() {
  return (
    <>
      <div onClick={this.changeRoute("page1")}>1</div>
      <div onClick={this.changeRoute("page2")}>2</div>
    </>
  )
}

4. If I use inline binding then it is also not best with performance

changeRoute (routeName) {
  console.log(routeName)
}
render() {
  return (
    <>
      <div onClick={this.changeRoute.bind(this, "page1")}>1</div>
      <div onClick={this.changeRoute.bind(this, "page2")}>2</div>
    </>
  )
}

Then how can I proceed with the best way passing parameters?

Ryan M
  • 18,333
  • 31
  • 67
  • 74
Dark Knight
  • 995
  • 4
  • 22
  • 48
  • 1
    It's probably worth noting that the *reason* why using inline arrow functions here "is not [a] good way in terms of performance" isn't because arrow functions would somehow be intrinsically slow to run (they're not, and in any case the cost of a function call is totally negligible for something as rarely executed as a click handler) but because React will create new instances of the functions every time the component is re-rendered. Inline binding has the exact same issue. And it's usually fine anyway, unless the component gets re-rendered *very* frequently. – Ilmari Karonen Apr 14 '20 at 18:14
  • 2
    @IlmariKaronen In most of the cases component re-renders frequently because the components have input field and typing & setting the e.target.value in state result in frequent render. – Dark Knight Apr 15 '20 at 05:14

4 Answers4

6

You can use arrow function to define your changeRoute handler.

This is known as Class field syntax. More on it here in official react docs.

constructor() {
  super(props)
}

changeRoute = (parameter) => (event) => {
    // business logic for route change.
}

Then you can use this function directly like so:

render() {
  return (
    <>
      <div onClick={changeRoute(params1)}>1</div>
      <div onClick={changeRoute(params2)}>2</div>
    </>
  )
}

You do not have to worry about the binding. Arrow functions inherit their parent's this.

Utsav Patel
  • 2,789
  • 1
  • 16
  • 26
  • @DarkKnight Your last comment was `executing on the go`. My answer is in response to that. I am trying to tell you that your click handler will not `execute on the go` if you define the handler like I have posted. – Utsav Patel Apr 14 '20 at 10:03
  • Pls check [this](https://stackoverflow.com/questions/29810914/react-js-onclick-cant-pass-value-to-method#comment78741749_45448802) – Dark Knight Apr 14 '20 at 10:04
  • @DarkKnight Please read this https://reactjs.org/docs/handling-events.html Class field syntax is one of the recommended methods by official react docs. – Utsav Patel Apr 14 '20 at 10:18
  • Is this the most efficient way of passing an argument? here function is called every time on re render. same as bind. How is it more efficient? – Manjunath Gk Oct 07 '21 at 19:30
  • Performance wise it is same as using bind. `public class fields syntax` is just syntactic sugar. You can read more on that here https://babeljs.io/docs/en/babel-plugin-proposal-class-properties. – Utsav Patel Oct 08 '21 at 04:30
  • this approach is not correct, function gets called while rendering – Haseeb Zulfiqar Aug 17 '22 at 12:00
4

You can add a data to your div:

<div data-id={1} onClick={this.changeRoute}>1</div>

Then you can retrieve that data in your onClick handler:

onClick = (event) => {
  const id = event.currentTarget.dataset.id;
}
HermitCrab
  • 3,194
  • 1
  • 11
  • 10
2

#1 is fine.

#2 is also 'fine', but you need to pass props, then the render function will look exactly like #1. You will be calling the bind'd function, because you replaced it in the constructor.

#3 is just wrong, as the function gets called during render.

And regarding #4, from react docs

We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.

This causes a performance penalty when your function is used in its child components and will cause the child components to re-render (its not in your case). So you shouldn't do #4.

Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
1

The best way currently is to wrap your event handler in useCallback hook as it will prevent your handler function from being created each time render is called.

import React, { useCallback } from 'react'

const MyComponent = ({ changeRoute }) => {
  const eventHandler = useCallback(() => {
    changeRoute('page1')
  }, [changeRoute])

  return (
    <div onClick={eventHandler}>1</div>
  )
}

For more info check - useCallback docs