react-infinite-scroll-hook
Version:
A simple hook to create infinite scroll components
127 lines (101 loc) • 3.62 kB
JavaScript
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;