Skip to content

Instantly share code, notes, and snippets.

@roginfarrer
Created November 11, 2024 17:16
Show Gist options
  • Save roginfarrer/006691dfe7c26800168e083574664116 to your computer and use it in GitHub Desktop.
Save roginfarrer/006691dfe7c26800168e083574664116 to your computer and use it in GitHub Desktop.
Reusable resize observer hook for React
import { useEffect } from "react";
/**
* Reusing a single instance of a ResizeObserver is better for performance
* than creating multiple instances. https://github.com/WICG/resize-observer/issues/59
*
* ReusableResizeObserver handles logic for collecting each observed entity
* and its associated callback
*
* @example
*
* ```js
* const observer = new ReusableResizeObserver();
* observer.add(someHTMLElement, (entry) => handleEntry(entry))
* observer.add(anotherHTMLElement, (entry) => handleAnotherEntry(entry))
* ```
*/
export class ReusableResizeObserver {
private observer: ResizeObserver;
public readonly listeners = new Map<
HTMLElement,
(entry: ResizeObserverEntry) => void
>();
constructor(Observer = ResizeObserver) {
this.observer = new Observer(this.handleEntries);
}
private handleEntries: ResizeObserverCallback = (entries) => {
for (const entry of entries) {
if (!(entry.target instanceof HTMLElement)) {
throw new Error();
}
const cb = this.listeners.get(entry.target);
if (cb) {
cb(entry);
}
}
};
add = (
element: HTMLElement,
callback: (entry: ResizeObserverEntry) => void,
options?: ResizeObserverOptions
) => {
this.listeners.set(element, callback);
this.observer.observe(element, options);
};
has = (element: HTMLElement) => {
return this.listeners.has(element);
};
remove = (element: HTMLElement) => {
this.listeners.delete(element);
this.observer.unobserve(element);
};
}
let observer: ReusableResizeObserver | undefined;
export function useResizeObserver(
target: HTMLElement | null,
callback: (entry: ResizeObserverEntry) => void
) {
useEffect(() => {
if (!target) return;
observer = observer ?? new ReusableResizeObserver();
if (observer.has(target)) {
observer.remove(target);
}
observer.add(target, callback);
return () => {
observer?.remove(target);
};
}, [callback, target]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment