Callback Ref

A callback ref (7.3) allows us to obtain the position and size of a DOM node. React will call that callback whenever the ref gets attached to a different node.


RESETRUNFULL
In this example, the callback ref will be called only when the component mounts and unmounts since the rendered <h1> component stays present throughout any rerenders. If you want to be notified any time a component resizes, you may want to use ResizeObserver or a third-party Hook built on it.

function MeasureExample() {
  const [width, setWidth] = React.useState(0);
  const measuredRef = React.useCallback(node => {
    if (node !== null) {
      setWidth(node.getBoundingClientRect().width);
    }
  }, []);
  return (
    <React.Fragment>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(width)}px wide.</h2>
    </React.Fragment >
  );}

useRef is not used in the example above because an object ref doesn't notify us about changes to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements:

This will result in a runtime error:<br> Uncaught TypeError: Cannot read property 'getBoundingClientRect' of null
RESETRUNFULL
function MeasureExample() {
   const wRef = React.useRef(null);
   return (
    <React.Fragment>
      <h1 ref={wRef}>Hello, world</h1>
      <h2>The above header is
                {Math.round(wRef.current.getBoundingClientRect().width)}px wide.</h2>
    </React.Fragment >
  );}ReactDOM.render(<MeasureExample/>,document.querySelector("div"));

We can extract the logic above into a reusable custom hook (Chapter 22):


RESETRUNFULL
function MeasureExample() {
  const [rect, ref] = useClientRect();
  return (
    <React.Fragment>
      <h1 ref={ref}>Hello, world</h1>
      {rect !== null &&
        <h2>The above header is {Math.round(rect.width)}px wide</h2>
      }
    </ React.Fragment >
  );}function useClientRect() {
  const [rect, setRect] = React.useState(null);
  const ref = React.useCallback(node => {
    if (node !== null) {
      setRect(node.getBoundingClientRect());
    }
  }, []);
  return [rect, ref];}