14

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?

2
  • 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. Commented Apr 14, 2020 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. Commented Apr 15, 2020 at 5:14

5 Answers 5

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.

Sign up to request clarification or add additional context in comments.

6 Comments

@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.
Pls check this
@DarkKnight Please read this reactjs.org/docs/handling-events.html Class field syntax is one of the recommended methods by official react docs.
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?
Performance wise it is same as using bind. public class fields syntax is just syntactic sugar. You can read more on that here babeljs.io/docs/en/babel-plugin-proposal-class-properties.
|
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;
}

1 Comment

Ha ha ha... Can be done. But the react himself does not have work around?
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.

Comments

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

2 Comments

The OP would appear to be using a class component, where you can't use hooks.
I hope my answer will help him to understand the better way
0

Below is another way to read parameter. Here you don't need to create another function inside the arrow function to pass parameter.

const Simple = () => {
    
    const handleClick = (e) => {
      e.preventDefault()
      let id = e.target.getAttribute('data-id')
      console.log(id) // prints hello
      console.log('This is handle click')
    }
    
    return (
      <>
        <a onClick={handleClick} data-id='hello'>Click here</a>
      </>
    )
    
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.