Created
January 28, 2021 03:36
-
-
Save its-jman/fa3330d1ff27d1818c4d06ee3d36c5b0 to your computer and use it in GitHub Desktop.
useMaxScroll.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { useEffect, useRef, useState } from "react"; | |
| import debounce from "lodash.debounce"; | |
| import useResizeObserver from "useResizeObserver"; | |
| const SINGLE_CHILD_WARNING = "Warning: Expected a single child node, aborting..."; | |
| const NO_NODE_WARNING = "Warning: useMaxPageScroll received an invalid node. Ignoring."; | |
| /** | |
| * Hook to prevent scrolling jitters. Watches the provided DOM node and | |
| * maintains a min-height of the distance to the bottom of the screen, relative to | |
| * your scroll position. | |
| * | |
| * This ensures that when nodes are removed from the page (eg. when filtering a table) | |
| * the page will not suddenly shrink. It will only shrink once you have scrolled up. | |
| * | |
| * @param _node {Document | React.Ref} | |
| */ | |
| export default function useMaxPageScroll(_node = document) { | |
| let node = _node; | |
| const isRef = node?.hasOwnProperty("current"); | |
| if (isRef) node = node.current; | |
| const aborted = useRef(false); | |
| const [expectedHeight, setExpectedHeight] = useState(0); | |
| const expectedHeightRef = useRef(expectedHeight); | |
| expectedHeightRef.current = expectedHeight; | |
| useResizeObserver(_node, (entry) => { | |
| if (aborted.current) return; | |
| setExpectedHeight(entry.contentRect.height); | |
| }); | |
| useEffect(() => { | |
| if (!isRef && !node) console.warn(NO_NODE_WARNING); | |
| if (node) { | |
| const listener = debounce((event) => { | |
| if (aborted.current) return; | |
| const scrollTarget = node === document ? node.scrollingElement : node; | |
| const scrollBottom = scrollTarget.clientHeight + scrollTarget.scrollTop; | |
| let heightTarget = node === document ? node.body : node.children; | |
| if (heightTarget.length !== undefined) { | |
| if (heightTarget.length !== 1) { | |
| console.error(SINGLE_CHILD_WARNING); | |
| aborted.current = true; | |
| return; | |
| } | |
| heightTarget = heightTarget[0]; | |
| } | |
| if (heightTarget) { | |
| heightTarget.style.minHeight = `${scrollBottom}px`; | |
| } | |
| }, 20); | |
| node.addEventListener("scroll", listener); | |
| return () => node.removeEventListener("scroll", listener); | |
| } | |
| }, [node]); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment