Search code examples
javascriptreactjsreact-hooksmemoization

Using useCallback in child component for parent callback


I have a component library used by several apps, and I use a callback passed in as a prop in useEffect. Is it Ok to wrap this in useCallback in this child component just in case the parent component did not? And also so the parent does not need to.

const Parent = () => {
  const cb = () => { ... };
  return <Widget condition={true} callback={cb} />
};

const Widget = ({condition, callback}) => {
  const safeCallback = useCallback(callback, [callback]);
  useEffect(() => {
     safeCallback();
  }, [condition, safeCallback]);
}

What if the parent DID use useCallback, and it happens twice?

const Parent = () => {
  const cb = () => { ... };
  const safeCb = useCallback(cb, [cb]);
  return <Widget condition={true} callback={safeCb} />
};

const Widget = ({condition, callback}) => {
  const safeCallback = useCallback(callback, [callback]);
  useEffect(() => {
     safeCallback();
  }, [condition, safeCallback]);
}

Solution

  • First of all you should ask yourself, if you really need the callback inside a useEffect. Maybe it can be refactored to be inside an event handler instead. You can read more about getting rid of effects here.

    If this is not an option the way, you can make sure, not to pass an unstable reference to a useEffect is this:

    const Widget = ({ callback, condition }) => {
      const callbackRef = useRef(callback);
      useLayoutEffect(() => {
        callbackRef.current = callback;
      }, [callback]);
    
      useEffect(() => {
        callbackRef.current();
      }, [condition]);
    }