/** * 출처: https://github.com/jaredLunde/react-hook */ import { ResizeObserver as Polyfill, ResizeObserverEntry, } from "@juggle/resize-observer"; import useLayoutEffect from "./usePassiveLayoutEffect"; import useLatest from "./useLatest"; // 브라우저 ResizeObserver를 사용할 건지 폴리필을 사용할 건지 결정 const ResizeObserver = typeof window !== "undefined" && "ResizeObserver" in window ? // @ts-ignore window.ResizeObserver : Polyfill; /** * A React hook that fires a callback whenever ResizeObserver detects a change to its size * * @param target A React ref created by `useRef()` or an HTML element * @param callback Invoked with a single `ResizeObserverEntry` any time * the `target` resizes */ function useResizeObserver( target: React.RefObject | T | null, callback: UseResizeObserverCallback ): Polyfill { const resizeObserver = getResizeObserver(); const storedCallback = useLatest(callback); useLayoutEffect(() => { let didUnsubscribe = false; const targetEl = target && "current" in target ? target.current : target; if (!targetEl) return () => {}; function cb(entry: ResizeObserverEntry, observer: Polyfill) { if (didUnsubscribe) return; storedCallback.current(entry, observer); } resizeObserver.subscribe(targetEl as HTMLElement, cb); return () => { didUnsubscribe = true; resizeObserver.unsubscribe(targetEl as HTMLElement, cb); }; }, [target, resizeObserver, storedCallback]); return resizeObserver.observer; } function createResizeObserver() { let ticking = false; let allEntries: ResizeObserverEntry[] = []; const callbacks: Map> = new Map(); const observer = new ResizeObserver( (entries: ResizeObserverEntry[], obs: Polyfill) => { allEntries = allEntries.concat(entries); if (!ticking) { window.requestAnimationFrame(() => { const triggered = new Set(); for (let i = 0; i < allEntries.length; i++) { if (triggered.has(allEntries[i].target)) continue; triggered.add(allEntries[i].target); const cbs = callbacks.get(allEntries[i].target); cbs?.forEach((cb) => cb(allEntries[i], obs)); } allEntries = []; ticking = false; }); } ticking = true; } ); return { observer, subscribe(target: HTMLElement, callback: UseResizeObserverCallback) { observer.observe(target); const cbs = callbacks.get(target) ?? []; cbs.push(callback); callbacks.set(target, cbs); }, unsubscribe(target: HTMLElement, callback: UseResizeObserverCallback) { const cbs = callbacks.get(target) ?? []; if (cbs.length === 1) { observer.unobserve(target); callbacks.delete(target); return; } const cbIndex = cbs.indexOf(callback); if (cbIndex !== -1) cbs.splice(cbIndex, 1); callbacks.set(target, cbs); }, }; } let _resizeObserver: ReturnType; const getResizeObserver = () => !_resizeObserver ? (_resizeObserver = createResizeObserver()) : _resizeObserver; export type UseResizeObserverCallback = ( entry: ResizeObserverEntry, observer: Polyfill ) => any; export default useResizeObserver;