@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
JavaScript
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