Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,61 @@ export function useCamera(): {
* handle that.
*/
export function useAutoUpdatingClientRect(): [DOMRect | null, (node: Element | null) => void] {
// This hooks returns `rect`.
const [rect, setRect] = useState<DOMRect | null>(null);
// Using state as ref.current update does not trigger effect hook when reset

const { ResizeObserver, requestAnimationFrame } = useContext(SideEffectContext);

// Keep the current DOM node in state so that we can create a ResizeObserver for it via `useEffect`.
const [currentNode, setCurrentNode] = useState<Element | null>(null);

// `ref` will be used with a react element. When the element is available, this function will be called.
const ref = useCallback((node: Element | null) => {
// track the node in state
setCurrentNode(node);
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
const { ResizeObserver } = useContext(SideEffectContext);

/**
* Any time the DOM node changes (to something other than `null`) recalculate the DOMRect and set it (which will cause it to be returned from the hook.
* This effect re-runs when the DOM node has changed.
*/
useEffect(() => {
if (currentNode !== null) {
// When the DOM node is received, immedaiately calculate its DOM Rect and return that
setRect(currentNode.getBoundingClientRect());
}
}, [currentNode]);

/**
* When scroll events occur, recalculate the DOMRect. DOMRect represents the position of an element relative to the viewport, so that may change during scroll (depending on the layout.)
* This effect re-runs when the DOM node has changed.
*/
useEffect(() => {
// the last scrollX and scrollY values that we handled
let previousX: number = window.scrollX;
let previousY: number = window.scrollY;

const handleScroll = () => {
requestAnimationFrame(() => {
// synchronously read from the DOM
const currentX = window.scrollX;
const currentY = window.scrollY;

if (currentNode !== null && (previousX !== currentX || previousY !== currentY)) {
setRect(currentNode.getBoundingClientRect());
}

previousX = currentX;
previousY = currentY;
});
};

window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [currentNode, requestAnimationFrame]);

useEffect(() => {
if (currentNode !== null) {
const resizeObserver = new ResizeObserver((entries) => {
Expand Down