Created
November 11, 2024 17:16
-
-
Save roginfarrer/006691dfe7c26800168e083574664116 to your computer and use it in GitHub Desktop.
Reusable resize observer hook for React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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