Coding, Insights, and Digital Discoveries 👩🏻💻
From Stay-at-Home Mom to a Developer After 50
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:
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
I enjoy digging into the source code to understand its structure and the logic driving it. Let’s dive in and break it down!
declare function useMeasure({ debounce, scroll, polyfill, offsetSize }?: Options): Result;
useMeasure
is a React hook.Options
object with four possible properties:debounce
: Controls update frequency. It accepts: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.declare type Result = [(element: HTMLOrSVGElement | null) => void, RectReadOnly, () => void]
useMeasure()
hook:(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.
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.
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.
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:
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).
useMeasure
is a dynamic version of useRef
hookAspect | useRef | useMeasure |
---|---|---|
Purpose | Stores a reference to a DOM element. | Tracks dimensions and dynamically reacts to size changes. |
Updates | Static, value only changes manually. | Dynamic, automatically updates on DOM changes. |
Key Use Case | Accessing a DOM node directly. | Monitoring layout changes for responsive or interactive behaviors. |
Complexity | Lightweight and simple. | Heavier, relies on ResizeObserver and optional debouncing. |
Cleanup | Not required for refs. | Requires cleanup to stop observing and free resources. |
useMeasure
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.
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.
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.
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.