Why arrow functions is not memoized between renders?

Viewed 629

Can you explain me why handleClick function is always new?

function Foo() {
  const handleClick = () => {
    console.log('Click happened');
  }

  return <Button onClick={handleClick}>Click Me</Button>;
}
3 Answers

Remember that Foo() is also a JavaScript function that's been re-run on every render.

Inside of that function there's a statement that is:

const handleClick = () => {...};

Meaning: create a function and assign it to my recently created local variable handleClick. Thus, it will be recreated on every render.

The performance impact of that is usually close to zero. But if you need, you can optmize for that.

const handleClick = useCallback(()=>{...},[dependencyArray]);

If that function depends on variables that may change, you can add those to the dependencyArray and React will recreate that functions if one of them changes. Otherwise it will keep the same function from the last time it was created.

From React Hooks FAQ

Are Hooks slow because of creating functions in render?

No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.

From: https://reactjs.org/docs/hooks-reference.html#usecallback

useCallback()

Returns a memoized callback.

Pass an inline callback and an array of dependencies. 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).

() => { console.log('Click happened'); } creates a new function.

You do nothing to preserve it between calls to the Foo function.

So every time Foo is called, you get a new function.

Because every time you render Foo, it will create everything inside of it again.

// rendering Foo
function Foo() {
  const handleClick = () => {    // instantiated again
    console.log('Click happened');
  }

  const something...     // instantiated again

  return <Button onClick={handleClick}>Click Me</Button>;
}

To avoid handleClick to be instantiated again you can declare it outside of the component.

const handleClick = () => {    
  console.log('Click happened');
}
function Foo() {              
  return <Button onClick={handleClick}>Click Me</Button>;
}

Or if You really want that to be in scope of the function but not instantiated you could do like this

// rendering Foo
function Foo() {
  const handleClick = useCallback( // not re-defined again, uses old reference
    () => {
      console.log(`Click happened`);
    }
  );

  const something...     // instantiated again

  return <Button onClick={handleClick}>Click Me</Button>;
}

Read about UseCallback here

Related