Coding, Insights, and Digital Discoveries 👩🏻‍💻

From Stay-at-Home Mom to a Developer After 50

Published on

Comprehensive Guide to the useMeasure Hook in React

useMeasure hook in react

What is useMeasure hook?

useMeasure is a utility hook from react-use-measure library. It simplifies the task of tracking the dimensions and position of a DOM element. Internally, it leverages the browser's ResizeObserver API to observe size changes in a performant manner.

This hook is commonly used in cases where layout changes need to dynamically adjust the UI, such as:

  • Responsive layouts
  • Dynamic content rendering based on size constraints
  • Managing animations or transitions tied to element dimensions

Codebase of useMeasure

This is the codebase from react-use-measure module:

declare type ResizeObserverCallback = (entries: any[], observer: ResizeObserver) => void
declare class ResizeObserver {
  constructor(callback: ResizeObserverCallback)
  observe(target: Element, options?: any): void
  unobserve(target: Element): void
  disconnect(): void
  static toString(): string
}
export interface RectReadOnly {
  readonly x: number
  readonly y: number
  readonly width: number
  readonly height: number
  readonly top: number
  readonly right: number
  readonly bottom: number
  readonly left: number
  [key: string]: number
}
declare type HTMLOrSVGElement = HTMLElement | SVGElement
declare type Result = [(element: HTMLOrSVGElement | null) => void, RectReadOnly, () => void]
export declare type Options = {
  debounce?:
    | number
    | {
        scroll: number
        resize: number
      }
  scroll?: boolean
  polyfill?: {
    new (cb: ResizeObserverCallback): ResizeObserver
  }
  offsetSize?: boolean
}
declare function useMeasure({ debounce, scroll, polyfill, offsetSize }?: Options): Result
export default useMeasure

3. Exploring the Codebase

I enjoy digging into the source code to understand its structure and the logic driving it. Let’s dive in and break it down!

1. Function Declaration:

declare function useMeasure({ debounce, scroll, polyfill, offsetSize }?: Options): Result;
  • useMeasure is a React hook.
  • It accepts an optional Options object with four possible properties:
  • debounce: Controls update frequency. It accepts:
  • A single number (milliseconds).
  • An object with specific settings for scroll and resize events.
  • scroll: When true, monitors layout changes caused by scrolling.
  • polyfill: Allows passing a fallback implementation of ResizeObserver when browsers don't support it.
  • offsetSize: Determines whether offsets (top, left) should be included in the measurements.

2. What it returns?

declare type Result = [(element: HTMLOrSVGElement | null) => void, RectReadOnly, () => void]
  • This tuple with three parts is returned by useMeasure() hook:
    1. Ref-setter Function: (element: HTMLOrSVGElement | null) => void.
    • It is used to attach the element. It allows the library to observe the specified element and track its size and position.

    • Setting null effectively disconnects the element, stopping measurements.

    1. RectReadOnly: An object with element dimensions such as width, height, top, left and other measurements. This object updates dynamically whenever the element's size or position changes.

    2. Cleanup Function: () => void. The function signature is () => void, which is the typical signature for a cleanup function. Cleanup functions are commonly used in useEffect to remove listeners, observers, or other resources that need to be cleared to prevent memory leaks. In this case, it is to disconnect the observer when it’s no longer needed, preventing memory leaks or performance issues.

3. Understanding ResizeObserver

ResizeObserver is a browser built-in API that tracks changes to the size of DOM elements. The react-use-measure hook internally uses a ResizeObserver to observe the changes in size of an element. .

Key Features:

  • Tracks size changes due to:
    • Resizing the browser window.
    • Dynamic content changes (e.g., images, text).
    • CSS modifications.
  • Provides a callback function to handle changes.

Example:

const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    console.log(entry.contentRect.width, entry.contentRect.height);
  });
});
observer.observe(document.getElementById('myElement'));

Polyfill: The useMeasure hook supports providing a custom implementation of ResizeObserver for environments where it isn’t natively available (e.g., legacy browsers or Node.js).

4. useMeasure is a dynamic version of useRef hook

AspectuseRefuseMeasure
PurposeStores a reference to a DOM element.Tracks dimensions and dynamically reacts to size changes.
UpdatesStatic, value only changes manually.Dynamic, automatically updates on DOM changes.
Key Use CaseAccessing a DOM node directly.Monitoring layout changes for responsive or interactive behaviors.
ComplexityLightweight and simple.Heavier, relies on ResizeObserver and optional debouncing.
CleanupNot required for refs.Requires cleanup to stop observing and free resources.

Some Examples of useMeasure

  1. Responsive layout:

In this example, we will use the useMeasure hook to make a component's layout responsive based on its width. As the container resizes, the layout adjusts accordingly.

import React from 'react';
import useMeasure from 'react-use-measure';

const ResponsiveLayout = () => {
  const [ref, bounds] = useMeasure();

  return (
    <div ref={ref} style={{ border: '1px solid black', padding: '10px' }}>
      <h2>Responsive Layout</h2>
      <div style={{ display: 'flex', flexDirection: bounds.width > 500 ? 'row' : 'column' }}>
        <div style={{ flex: 1, margin: '5px', backgroundColor: '#f0f0f0' }}>Box 1</div>
        <div style={{ flex: 1, margin: '5px', backgroundColor: '#d0d0d0' }}>Box 2</div>
      </div>
      <p>Width: {bounds.width}px</p>
    </div>
  );
};

export default ResponsiveLayout;

The layout uses flexDirection to adjust from a row to a column based on the container's width (bounds.width). If the width is greater than 500px, the elements are displayed side by side, otherwise, they stack vertically.

  1. Dynamic Content Rendering Based on Size Constraints

In this example, we use the useMeasure hook to conditionally render different content based on the size of the container. The content changes dynamically as the container's size adjusts.

import React from 'react';
import useMeasure from 'react-use-measure';

const DynamicContent = () => {
  const [ref, bounds] = useMeasure();

  return (
    <div ref={ref} style={{ border: '1px solid black', padding: '20px' }}>
      <h2>Dynamic Content Rendering</h2>
      {bounds.width < 300 ? (
        <p>Small container: Showing short content</p>
      ) : (
        <p>Large container: Showing more detailed content with extra information</p>
      )}
      <p>Width: {bounds.width}px</p>
    </div>
  );
};

export default DynamicContent;

The content rendered inside the container changes depending on its width (bounds.width). If the width is less than 300px, the component displays short content, otherwise, it displays more detailed content.

  1. Managing Animations or Transitions Tied to Element Dimensions

In this example, we use the useMeasure hook to trigger an animation that is based on the element's size. As the element's width changes, the animation will adjust its properties accordingly.

import React, { useState, useEffect } from 'react';
import useMeasure from 'react-use-measure';

const AnimatedElement = () => {
  const [ref, bounds] = useMeasure();
  const [scale, setScale] = useState(1);

  useEffect(() => {
    if (bounds.width > 500) {
      setScale(1.5); // Enlarge the element if its width is greater than 500px
    } else {
      setScale(1); // Reset the size if it's smaller
    }
  }, [bounds.width]);

  return (
    <div
      ref={ref}
      style={{
        width: '100%',
        height: '200px',
        backgroundColor: '#88f',
        transform: `scale(${scale})`,
        transition: 'transform 0.3s ease',
      }}
    >
      <h2 style={{ color: '#fff' }}>Animated Element</h2>
      <p>Width: {bounds.width}px</p>
    </div>
  );
};

export default AnimatedElement;

The element’s scale is dynamically adjusted based on its width (bounds.width). If the width exceeds 500px, the element is enlarged using the transform: scale() CSS property. The transition effect smoothly adjusts the scale when the width changes.

  1. An example using cleanup function:
import React from 'react';
import useMeasure from 'react-use-measure';

function App() {
  const [ref, rect, stopObserving] = useMeasure();

  return (
    <div>
      <div ref={ref} style={{ resize: 'both', overflow: 'auto', border: '1px solid black', width: 200, height: 200 }}>
        Resize me!
      </div>
      <p>Width: {rect.width}px</p>
      <p>Height: {rect.height}px</p>
      <button onClick={stopObserving}>Stop Observing</button>
    </div>
  );
}

With useMeasure, we gain a powerful and responsive way to handle DOM size changes in modern React applications.

← See All Posts