Skip to content

Commit

Permalink
Refactor useEffect for handling scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
kqualters-elastic committed Jul 20, 2020
1 parent a47d047 commit 8838dea
Showing 1 changed file with 44 additions and 16 deletions.
60 changes: 44 additions & 16 deletions x-pack/plugins/security_solution/public/resolver/view/use_camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,33 +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);
const [currentY, setCurrentY] = useState<number>(0);
const [currentX, setCurrentX] = useState<number>(0);

// `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);
const handleScroll = useCallback(() => {
if (currentX !== window.scrollX || currentY !== window.scrollY) {
if (currentNode !== null) {
setRect(currentNode.getBoundingClientRect());
setCurrentY(window.scrollY);
setCurrentX(window.scrollX);
}

/**
* 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());
}
}, [currentX, currentY, setCurrentX, setCurrentY, currentNode]);
}, [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);
};
}, [handleScroll]);
}, [currentNode, requestAnimationFrame]);

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

0 comments on commit 8838dea

Please sign in to comment.