Johnson Samuel
Johnson Samuel

Reputation: 2076

Function inside functional component in React hooks - Performance

Need suggestion on having function within a functional component in react Hooks.

As far as I researched, many are saying it is bad practice because it creates nested/inner function every time we call re-render. After doing some analysis,

I found we can use onClick={handleClick.bind(null, props)} on the element and place the function outside the functional component.

Example:

const HelloWorld = () => {
  function handleClick = (event) => {
    console.log(event.target.value);
  }

  return() {
    <>
        <input type="text" onChange={handleClick}/>
    </>
  }
}

Please advise if there is any alternative way.

Thanks in advance.

Upvotes: 37

Views: 31720

Answers (8)

Marco Scabbiolo
Marco Scabbiolo

Reputation: 7449

Just useCallback

Why would you need to define a function inside a component and not anywhere else? Because you either have to pass it to another child compononent, or you have to use it in an effect, memo, or another callback. For any of those cases if you dont wrap your function in useCallback you will be passing a new function and causing the component to rerender, the memo to re-run, the effect to re-run, or the callback to re-define.

You can never avoid the performance hit of redefining the function itself, but you can avoid the performance hit of performing any computation that has that function as a dependency to know if it has to run or not (be it a component or hook).

So... just wrap every function in your component in useCallback and forget about it, never seen a single in case in which this would cause any harm. If you can define the function outside the component, thats always better.

Upvotes: 0

ArneHugo
ArneHugo

Reputation: 6509

Don't worry about it

Don't worry about creating new functions on each render. Only in edge cases does that impede your performance. Setting onClick handlers are not one of those, so just create a new function on each render.

However, when you need to make sure you use the same function every time, you can use useCallback

Why not use useCallback for onClick

Here is a reason why you shouldn't bother with useCallback for onClick handlers (and most other event handlers).

Consider the following code snippets, one without useCallback:

function Comp(props) {
  return <button onClick={() => console.log("clicked", props.foo)}>Text</Button>
}

and one with useCallback:

function Comp(props) {
  const onClick = useCallback(() => {
    console.log("clicked", props.foo)
  }, [props.foo])

  return <button onClick={onClick}>Text</Button>
}

The only difference in the latter is that React doen's have to change the onClick on your button if props.foo remains the same. Changing the callback is a very cheap operation, and it's not at all worth complicating your code for the theoretical performance improvement it gives.

Also, it's worth noting that a new function is still created on every render even when you use useCallback, but useCallback will return the old one as long as the dependencies passed as the second argument are unchanged.

Why ever use useCallback

The point of using useCallback is that if you compare two functions with reference equality, fn === fn2 is true only if fn and fn2 point to the same function in memory. It doesn't matter if the functions do the same.

Thus, if you have memoisation or otherwise only run code when the function changes, it can be useful to use useCallback to use the same function again.

As an example, React hooks compare old and new dependencies, probably using Object.is.

Another example is React.PureComponent, which will only re-render when props or state have changed. This can be useful for components that use a lot of resources to render. Passing e.g. a new onClick to a PureComponent on each render will cause it to re-render every time.

Upvotes: 39

Sava B.
Sava B.

Reputation: 1047

I would honestly just use a class component in these cases. I'm aware of premature optimization, but creating a new function each time does just seem like extravagant wastefulness without much of a maintainability upside. tibbus has demonstrated the perf hit, and inline functions are arguably less readable than class methods. All you're losing out is the slick feeling of writing a functional component.

Upvotes: 0

cchiaramelli
cchiaramelli

Reputation: 301

Inspired by @tibbus 's benchmark, I made this one that tests the perfomance using or not the useCallback hook. After several executions, it seams that the use of useCallback can be very important for high frequency rendering.

Execution 1

enter image description here

Execution 2

enter image description here

Execution 3

enter image description here

https://codesandbox.io/s/usecallback-vs-raw-definition-xke9v?file=/src/App.js

Upvotes: 4

Tiberiu Popescu
Tiberiu Popescu

Reputation: 4524

enter image description here

Interesting question, me and my coleagues had some worries about this, so I did a test.

I have created 1 Component with Hooks and 1 Component with Class, put some functions there and then render it 1000x times.

The Component with Class looks like this:

export class ComponentClass extends React.PureComponent {
  click1 = () => {
    return console.log("just a log");
  };

  render() {
    return (
      <>
        <span onClick={this.click1}>1</span>
      </>
    );
  }
}

The Component with Hooks looks like this:

export const ComponentHook = React.memo((props) => {
  const click1 = () => {
    return console.log("just a log");
  };

  return (
    <>
      <span onClick={click1}>1</span>
    </>
  );
});

I have added more click handlers to the components and then rendered them some 1000s times, the Class is faster as it does not define the functions each render, if you increase the number of functions defined, then the difference will be bigger:

Here it is a codesandbox so you can test the performance Class vs Hooks : https://codesandbox.io/s/hooks-vs-class-forked-erdpb

Upvotes: 11

Alireza HI
Alireza HI

Reputation: 1933

useCallback

You can use useCallback feature :

const HelloWorld = ({ dispatch }) => {
  const handleClick = useCallback((event) => {
    dispatch(() => {console.log(event.target.value)});
  })

  return() {
    <>
        <input type="name" onChange={handleClick}/>
    </>
  }
}

useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

For further details visit react documentation reference: React useCallback


Old Solution

First solution: To pass the your handleClick function to your functional component.

const HelloWorld = (props) => {

  return() {
    <>
        <input type="name" onChange={props.handleClick}/>
    </>
  }
}

Second solution: To define your function outside of your functional component.

Upvotes: 8

Dhruvil21_04
Dhruvil21_04

Reputation: 2189

As per React Documentation (ending part),

The problem with latter syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.

Class field syntax:

class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

arrow function in the callback syntax:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // This syntax ensures `this` is bound within handleClick
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

Upvotes: 0

Jonas Wilms
Jonas Wilms

Reputation: 138267

many are saying it is bad practice because it creates nested/inner function every time we call re-render

No, inner functions / closures are so common, there is no problem with them. The engine can heavily optimize those.

The point here is that you pass the function as a prop to the child component. And as the function was "recreated", it does not equal the previous function passed, annd thus the child does rerender (and that's whats bad for performance).

You can resolve that with useCallback, which memoizes the function reference.

Upvotes: 13

Related Questions