Hone logo
Hone
Problems

React Custom Hook: useResizeObserver

This challenge focuses on building a robust and reusable React custom hook, useResizeObserver. This hook will leverage the browser's ResizeObserver API to efficiently track changes in an element's dimensions and provide those dimensions to your React components. This is incredibly useful for responsive design, dynamic layout adjustments, and performance optimizations where you need to react to visual changes in the DOM.

Problem Description

Your task is to create a custom React hook named useResizeObserver that accepts a React RefObject pointing to a DOM element. The hook should:

  1. Observe the element: Use the ResizeObserver API to observe the dimensions (width and height) of the referenced DOM element.
  2. Return dimensions: Provide the current width and height of the observed element as state within the hook.
  3. Update on resize: Automatically update the returned dimensions whenever the observed element's size changes.
  4. Handle cleanup: Ensure that the ResizeObserver is properly disconnected when the component unmounts or the ref changes to prevent memory leaks.

Key Requirements:

  • The hook should be written in TypeScript.
  • It should accept a RefObject<Element> as an argument.
  • It should return an object containing width and height (both numbers).
  • The hook should return 0 for both width and height initially, or until the element is rendered and observed.
  • The ResizeObserver should be initialized with a callback that updates the hook's state.
  • The observer should be disconnected in a useEffect cleanup function.

Expected Behavior:

When a component uses this hook and passes a ref to an element, the hook should return the element's current dimensions. As the element is resized (e.g., by the browser window resizing, or CSS changes), the hook should automatically re-render the consuming component with the updated width and height.

Edge Cases to Consider:

  • Element not yet rendered: The initial state should reflect that the element is not yet measured.
  • Ref changes: If the ref object itself is updated to point to a different element, the old observer should be disconnected and a new one created.
  • Element removed: If the referenced element is removed from the DOM, the observer should be disconnected.

Examples

Example 1: Basic Usage

Let's imagine a component that displays the dimensions of a div.

import React, { useRef } from 'react';
import useResizeObserver from './useResizeObserver'; // Assuming your hook is in this file

function ResizableBox() {
  const boxRef = useRef<HTMLDivElement>(null);
  const { width, height } = useResizeObserver(boxRef);

  return (
    <div>
      <div
        ref={boxRef}
        style={{
          width: '50%', // Example responsive width
          height: '200px',
          backgroundColor: 'lightblue',
          resize: 'both', // Allow manual resizing for testing
          overflow: 'auto',
        }}
      >
        Resize me!
      </div>
      <p>Current Dimensions: {width}px x {height}px</p>
    </div>
  );
}

Input to the hook: useRef<HTMLDivElement>(null) pointing to the div.

Expected Output (initial render): The useResizeObserver hook would initially return { width: 0, height: 0 }.

Expected Output (after render and resize): Assuming the div renders and has a computed width of 400px and a height of 200px: { width: 400, height: 200 }

Explanation: The hook attaches a ResizeObserver to the div. When the element is painted and its dimensions are known, the observer's callback fires, updating the hook's internal state. The ResizableBox component re-renders with the new dimensions.

Example 2: Handling Null Ref

If the ref is initially null or null is passed.

import React from 'react';
import useResizeObserver from './useResizeObserver';

function ComponentWithNullRef() {
  // Intentionally not assigning a ref here
  const { width, height } = useResizeObserver(null as unknown as React.RefObject<HTMLDivElement>);

  return (
    <div>
      <p>Dimensions for null ref: {width}px x {height}px</p>
    </div>
  );
}

Input to the hook: null (or a ref object that is currently null).

Expected Output: { width: 0, height: 0 }

Explanation: The hook should gracefully handle a null ref by not attempting to create an observer and by returning default values.

Constraints

  • The hook must be written in TypeScript.
  • It should not rely on any external libraries for the ResizeObserver functionality.
  • Performance is important: the ResizeObserver callback should be efficient, only updating state when necessary.
  • The hook should work correctly within React's lifecycle (e.g., useEffect for setup and cleanup).

Notes

  • The ResizeObserver API provides an entries array in its callback. You will likely want to access entry.contentRect.width and entry.contentRect.height.
  • Consider how you will handle the initial state before the element has been measured.
  • Make sure to correctly type the RefObject argument.
  • Think about the useEffect dependency array to ensure the observer is re-attached if the ref target changes (though RefObject itself doesn't typically change in a way that would trigger a useEffect if its .current property is what's being observed). The primary concern is proper cleanup when the component unmounts.
Loading editor...
typescript