UNPKG

react-infinite-scroll-hook

Version:
127 lines (101 loc) 3.62 kB
import { useEffect, useRef, useState } from "react"; import useWindowSize from "./useWindowSize"; import useInterval from "./useInterval"; var WINDOW = "window"; var PARENT = "parent"; function useInfiniteScroll(_ref) { var loading = _ref.loading, hasNextPage = _ref.hasNextPage, onLoadMore = _ref.onLoadMore, _ref$threshold = _ref.threshold, threshold = _ref$threshold === undefined ? 150 : _ref$threshold, _ref$checkInterval = _ref.checkInterval, checkInterval = _ref$checkInterval === undefined ? 200 : _ref$checkInterval, _ref$scrollContainer = _ref.scrollContainer, scrollContainer = _ref$scrollContainer === undefined ? WINDOW : _ref$scrollContainer; var ref = useRef(); var _useWindowSize = useWindowSize(), windowHeight = _useWindowSize.height, windowWidth = _useWindowSize.width; // Normally we could use the "loading" prop, but when you set "checkInterval" to a very small // number (like 10 etc.), some request components can't set its loading state // immediately (I had this problem with react-apollo's Query component. In some cases, it runs // "updateQuery" twice). Thus we set our own "listen" state which immeadiately turns to "false" on // calling "onLoadMore". var _useState = useState(true), listen = _useState[0], setListen = _useState[1]; useEffect(function () { if (!loading) { setListen(true); } }, [loading]); function getParentSizes() { var parentNode = ref.current.parentNode; var parentRect = parentNode.getBoundingClientRect(); var top = parentRect.top, bottom = parentRect.bottom, left = parentRect.left, right = parentRect.right; return { top: top, bottom: bottom, left: left, right: right }; } function getBottomOffset() { var rect = ref.current.getBoundingClientRect(); var bottom = rect.bottom; var bottomOffset = bottom - windowHeight; if (scrollContainer === PARENT) { var _getParentSizes = getParentSizes(), parentBottom = _getParentSizes.bottom; // Distance between bottom of list and its parent bottomOffset = bottom - parentBottom; } return bottomOffset; } function isParentInView() { var parent = ref.current ? ref.current.parentNode : null; if (parent) { var _getParentSizes2 = getParentSizes(), left = _getParentSizes2.left, right = _getParentSizes2.right, top = _getParentSizes2.top, bottom = _getParentSizes2.bottom; if (left > windowWidth) { return false; } else if (right < 0) { return false; } else if (top > windowHeight) { return false; } else if (bottom < 0) { return false; } } return true; } function listenBottomOffset() { if (listen && !loading && hasNextPage) { if (ref.current) { if (scrollContainer === PARENT) { if (!isParentInView()) { // Do nothing if the parent is out of screen return; } } // Check if the distance between bottom of the container and bottom of the window or parent // is less than "threshold" var bottomOffset = getBottomOffset(); var validOffset = bottomOffset < threshold; if (validOffset) { setListen(false); onLoadMore(); } } } } useInterval(function () { listenBottomOffset(); }, // Stop interval when there is no next page. hasNextPage ? checkInterval : 0); return ref; } export default useInfiniteScroll;