UNPKG

@rooks/use-boundingclientrect-ref

Version:

A hook that tracks the boundingclientrect of an element. It returns a callbackRef so that the element node if changed is easily tracked.

101 lines (96 loc) 2.89 kB
import { useState, useEffect, useCallback, useMemo } from 'react'; var config = { attributes: true, characterData: true, subtree: true, childList: true }; /** * * useMutationObserverRef hook * * Returns a mutation observer for a React Ref and fires a callback * * @param {MutationCallback} callback Function that needs to be fired on mutation * @param {MutationObserverInit} options */ function useMutationObserverRef(callback, options = config) { const [node, setNode] = useState(null); useEffect(() => { // Create an observer instance linked to the callback function if (node) { const observer = new MutationObserver(callback); // Start observing the target node for configured mutations observer.observe(node, options); return () => { observer.disconnect(); }; } }, [node, callback, options]); const ref = useCallback((node) => { setNode(node); }, []); return [ref]; } /** * Credit to material-ui for this snippet */ function setRef(ref, value) { if (typeof ref === "function") { ref(value); } else if (ref) { ref.current = value; } } /** * useForkRef * Joins refs together and returns a combination of the two as a new ref * @param refA * @param refB */ function useForkRef(refA, refB) { /** * This will create a new function if the ref props change and are defined. * This means react will call the old forkRef with `null` and the new forkRef * with the ref. Cleanup naturally emerges from this behavior */ return useMemo(() => { if (refA == null && refB == null) { return null; } return (refValue) => { setRef(refA, refValue); setRef(refB, refValue); }; }, [refA, refB]); } /** * @param element HTML element whose boundingclientrect is needed * @return ClientRect */ function getBoundingClientRect(element) { return element.getBoundingClientRect(); } /** * useBoundingclientrectRef hook * @return [CallbackRef | null, ClientRect | DOMRect | null, () => void] */ function useBoundingclientrectRef() { const [value, setValue] = useState(null); const [node, setNode] = useState(null); const update = useCallback(() => { setValue(node ? getBoundingClientRect(node) : null); }, [node]); useEffect(() => { update(); }, [node]); const ref = useCallback((node) => { setNode(node); }, []); const [mutationObserverRef] = useMutationObserverRef(update); const forkedRef = useForkRef(ref, mutationObserverRef); return [forkedRef, value, update]; } export default useBoundingclientrectRef; //# sourceMappingURL=index.esm.js.map