DEV Community

solring
solring

Posted on

[React newbie] Prevent redundant re-rendering when calling callbacks in useEffect(useLayoutEffect)

When passing callbacks to child components to detect their changes, you may need to call the callbacks inside useEffect or useLayoutEffect and add the callbacks into its dependency array:

 useLayoutEffect(() => {
    if(onScroll) onScroll(x, pos) // call the callback
  }, [x, pos, onScroll])
Enter fullscreen mode Exit fullscreen mode

However, this will cause an infinite loop when rendering if the callback will trigger the re-render of the parent component--even if it is just setting some states.

  const onScroll = (x, y) => {
    setScroll([x, y])
  }
Enter fullscreen mode Exit fullscreen mode

The onScroll function(object) will be recreated in every re-render, consequently triggering the calling of itself since it's in the dependency list, and going on and on......boom.
Example error message

You can simply remove the callback from the dependency list to fix the problem, but you will get the react-hooks/exhaustive-deps warning if you're using the lint.

Another solution is to use useCallback to memorize the callback function to prevent it from being recreated.

  const [scroll, setScroll] = useState([0, 0]
...
  const onScroll = useCallback((x, y) => {
    setScroll([x, y])
  }, [setScroll])
Enter fullscreen mode Exit fullscreen mode

According to official doc, the state setting function created by setState won’t change on re-renders, so it can be used in the dependency list to retain the identity of the callback function.

Discussion (0)