Avoid Listening for Ref Changes in useEffect – Use a Ref Callback Instead

Avoid Listening for Ref Changes in useEffect – Use a Ref Callback Instead

When working with refs in React, it's crucial to understand how updates to a ref's current property behave. A common mistake developers make is trying to use useEffect to track changes in a ref. However, React does not notify about changes in ref.current, making this approach ineffective.

Why Listening for Ref Changes in useEffect Doesn't Work

Consider the following example:

function App() {
  const [height, setHeight] = React.useState(0);
  const elementRef = React.useRef(null);

  React.useEffect(() => {
    if (elementRef.current) {
      setHeight(elementRef.current.offsetHeight);
    }
  }, [elementRef.current]); // ❌ Incorrect dependency

  return <div ref={elementRef}></div>;
}        

What's Wrong?

  • The dependency array [elementRef.current] does not trigger a re-run of useEffect when current changes.
  • useRef maintains the same object, but current updates internally without triggering a re-render.

The Correct Approach: Using a Ref Callback

A better way to handle this is by using a ref callback with useCallback:

function App() {
  const [height, setHeight] = React.useState(0);

  const elementRef = React.useCallback(node => {
    if (node !== null) {
      setHeight(node.offsetHeight);
    }
  }, []);

  return <div ref={elementRef}></div>;
}        

Why This Works

  • React calls the ref callback with the DOM element when it mounts and null when it unmounts.
  • This ensures setHeight runs whenever the element changes.

By using a ref callback, you ensure proper handling of DOM element changes without unnecessary re-renders or stale values.

To view or add a comment, sign in

More articles by Daniil Pronchenkov

Explore content categories