Vedant
Vedant

Reputation: 21

useCallback on function passed to a leaf element in react?

If a component has a leaf element (like button) that takes an onClick function which is coming from a custom hook, should that onClick be wrapped in useCallback?

Folder Structure

- someComponent (folder)
  
   SomeComponent.tsx (file)

   - hooks (folder)

    useSomeComponentClick.ts
//SomeComponent

const handleClick = useSomeComponentClick();

return (
  <button onClick={handleClick} />
);

// useSomeComponentClick

const useSomeComponentClick = () => {
   const handleClick = () => {
      // do something on click
   }

   return handleClick;
}
 

Now, should be wrap handleClick in useCallback?

Upvotes: -1

Views: 65

Answers (2)

WeDoTheBest4You
WeDoTheBest4You

Reputation: 1903

It would optimise the code if we wrapt it. However, we should be clear on it implications as well. Otherwise it may cause undesired rendering behaviours.

Short answer

Irrespective of a handler is attached to leaf component or returned by custom hook, if a handler needs to access the latest reactive values in a component, the handler must be redefined always with respect to the reactive values - props and states.

Explanation

This question may be better answered if we discuss it along with effects.

As we know, the fundamental difference between an effect and an event is that the handler in an effect is reactive. On the contrary, the handler in an event is not reactive. Reactive means, it should respond to the changes of props and states in a component. However, this difference is limited to the reactive nature of the two handlers.

As far as scoping of reactive values are concerned, the two handlers have the same nature. It means, by default, an event handler defined inside a component is redefined on every render, so that it gets access to the latest props and states. Similarly, an effect handler without a dependency array, is redefined on every render for the same purpose. However, there is a subtle difference.

The difference is that, an effect handler can be restricted to redefine with respect to its dependencies. Still it is optional. The two handlers in the following code redefined in every render. It does so since useEffect has no dependency array.

function Component() {
   const [state1, setSate1] = useState();
   const [state2, setSate1] = useState();

   function handlerClick(e) { 
      // some codes to write here
   }

   useEffect(()=>{
      // some codes to write here
   })

}

However, the below code makes a difference. While handler handlerClick is redefined in every change in the two states, the effect handler is redefined only on changing state1. This is because of the given dependency array.

function Component() {
   const [state1, setSate1] = useState();
   const [state2, setSate1] = useState();

   function handlerClick(e) { 
      // some codes to write here
   }

   useEffect(()=>{
      // some codes to write here
   },[state1])

}

We can make both handlers redefined in the same way. The below code does the same. The following code redefines the the two handlers on the same dependent state, state1. It is to take note that state2 in these two handlers will be stale or outdated. This is the implication of using useCallback.

function Component() {
  const [state1, setSate1] = useState();
  const [state2, setSate2] = useState();

  const handlerClick = useCallback(
    function handlerClick(e) {
      // some codes to write here
    },
    [state1]
  );

  useEffect(() => {
    // some codes to write here
  }, [state1]);

  return <>nothing special</>;
}

Answer to the question

Therefore, irrespective of a handler is attached to leaf component or returned by custom hook, if a handler needs to access the latest reactive values in a component, the handler must be redefined with respect to the reactive values - props and states.

Upvotes: 0

Jakub Kotrs
Jakub Kotrs

Reputation: 6229

useCallback is an optimisation technique. It is generally used to prevent re-renders or other recalculations.

In you very specific case, there is nothing to optimize, the code will be slower when you use it instead because

const handleClick = () => {
   // do something on click
}

is by itself more performant than

const handleClick = useCallback(() => {
   // do something on click
}, [])

these is less code to execute.

However, if the callback was used as a dependency of useMemo or useEffect or if it was passed to memo(Component) or React.PureComponent, it will make a difference.

Upvotes: 1

Related Questions