Search code examples
javascriptreactjsreact-hooksanonymous-functionusecallback

Questions about useCallback hook and anonymous function


When passing a callback function, especially when passing a parameterized function, I know that I should use the useCallback hook because the use of anonymous functions can adversely affect performance.

the example of anonymous function I said is like this.

import React, { useState } from 'react';

const Component = () => {
  const [param, setParam] = useState('');
  ...

  return (
    ...
    <SomeComponent
      onClick={() => setParam('parameter')}
      {...others}
    />
  );
}

In the process of converting an anonymous function to use this hook, I encountered an error saying 'Too many renders' or it didn't work properly. But I don't know exactly in what situation and in what situation.

and I used useCallback like below.

import React, { useState, useCallback } from 'react';

const Component = () => {
  const [param, setParam] = useState('');

  const handleClick = useCallback((params) => {
    setParam(params);
  },[]);

  ...
  return (
    ...
    <SomeComponent
      onClick={handleClick('parameter')}
      {...others}
    />
  );
}

However, when using an anonymous function to return within useCallback, it also worked.

This means code like here. (Only the differences compared to the code above.)

  const handleClick = useCallback((params) => {
    return () => setParam(params);
  },[]);

In this situation, I wonder if it's worse than just using an anonymous function inside the useCallback if I simply use an anonymous function instead of using this hook.


Solution

  •   const handleClick = useCallback((params) => {
        setParam(params);
      },[]);
    
      ...
      return (
        ...
        <SomeComponent
          onClick={handleClick('parameter')}
          {...others}
        />
      );
    

    in the above code during first render, at this statement "onClick={handleClick('parameter')}" handleClick function is called with a string named "parameter". since handleClick has setParam("parameter"), it will update state. updating state will cause re-render which will again come to same statement "onClick={handleClick('parameter')}" causing infinte loop.

    following code you added later works because you are not updating state, instead returning a function, which acts as onclick handler.

    const handleClick = useCallback((params) => {
        return () => setParam(params);
      },[]);
    

    better way of doing this shoud be as follwing,

    import React, { useState, useCallback } from 'react';
    
    const Component = () => {
      const [param, setParam] = useState('');
    
      const handleClick = useCallback((params) => {
        setParam(params);
      },[]);
    
      ...
      return (
        ...
        <SomeComponent
          onClick={handleClick}
          {...others}
        />
      );
    }
    

    coming back to your question , comparing performance depends on the other function definitions and render times of child compoents inside return function inside your Compoenent. let's say you have another onclickHanldier named 'anotherHandleClick' inside your app. then your component looks like this

    const Component = () => {
      const [param, setParam] = useState('');
      const [anotherParam, setAnotherParam] = useState('');
    
      const handleClick = (params) => {
        setParam(params);
      };
    const anotherHandleClick =(params) => {
        setAnotherParam(params);
      };
      ...
      return (
        ...
        <SomeComponent
          onClick={handleClick('parameter')}
          {...others}
        />
    <SomeComponent
          onClick={antherHandleClick('parameter')}
          {...others}
        />
      );
    }
    

    in the above component when any one of "SomeCompoenent" clicked entiere "Component" re-renders, so the handler functions are newly defined.and when both 's does referential equality check on onclick handler functions, they believe it is new handler function which casues them to render both. in that case it is better to use useCallBack hook as following,

    const Component = () => {
      const [param, setParam] = useState('');
          const [anotherParam, setAnotherParam] = useState('');
    
      const handleClick = useCallback((params) => {
        setParam(params);
      },[]);
    const anotherHandleClick = useCallback((params) => {
        setAnotherParam(params);
      },[]);
      ...
      return (
        ...
        <SomeComponent
          onClick={handleClick('parameter')}
          {...others}
        />
    <SomeComponent
          onClick={antherHandleClick('parameter')}
          {...others}
        />
      );
    }
    

    in the above code when any one is clicked , state changes. then when rendering, useCallback make sure that onclick handler refernces didn't change. so having dependency of onclick handlers won't be rerendered.

    so final thought is It's creating a function on each render in both cases. the second (because it'e wrapped in a useCallback) will return the memoized function created on the initial render

    when to use useMemo or useCallback refer this