Lior Erez
Lior Erez

Reputation: 1942

React hook useCallback without dependencies

Does it make sense to use useCallback without deps for simple event handlers?

For example:

const MyComponent = React.memo(() => {
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  const handleOtherClick = () => {
    console.log('clicked');
  };

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <button onClick={handleOtherClick}>Click me too</button>
    </div>
  );
});

What are the pros and cons for using useCallback in this case?

Upvotes: 30

Views: 29761

Answers (2)

S K
S K

Reputation: 1

const useIndependentCallback = <A extends unknown[], R>(callback: (...args: A) => R) => {
  const callbackRef = useRef(callback)
  callbackRef.current = callback
  return useCallback((...args: A): R => callbackRef.current(...args), [])
}

I do this when an independent callback is needed.

Upvotes: 0

skyboyer
skyboyer

Reputation: 23705

Purpose of useCallback does not depend on if you have dependencies or not. It's to ensure referential integrity. To get better performance. If you need that.

Because for flow having just function or function expression itself make code works well(I mean it does not require us to do any extra action to say referencing actual props etc). So useCallback is only about performance.

Say we render pure component(instance of React.PureComponent or functional component wrapped into React.memo)

function MyComponent() {
  const onChangeCallback = ...
  return <SomePureComponent onChange={onChangeCallback} />;
}

here if onChangeCallback is declared as just a function or arrow expression it will be re-created on each render. So it will be referentially different. And nested child will be re-rendered each time while it does not have to.

Another case is listing this callback as dependency in other useCallback, useMemo, useEffect.

function MyComponent() {
  const onChangeCallback = ...;
  return <Child onChange={onChangeCallback} />
}

...
function Child({onChange}) {
  useEffect(() => {
    document.body.addEventListener('scroll', onChange);
    return () => document.body.removeEventListener('scroll', onChange);
  }, [onChange]);
}

Here we also will have referentially different onChange in Child without useCallback. So useEffect will be run each time parent MyComponent is called. While we don't need it doing this.

So yes, having empty dependencies list when you don't have actually any dependency is better then declaring function inline without useCallback at all.

Upvotes: 38

Related Questions