react-infinite-scroll-hook
Version:
A simple hook to create infinite scroll components
140 lines (107 loc) • 3.98 kB
JavaScript
;
exports.__esModule = true;
var _react = require("react");
var _useWindowSize2 = require("./useWindowSize");
var _useWindowSize3 = _interopRequireDefault(_useWindowSize2);
var _useInterval = require("./useInterval");
var _useInterval2 = _interopRequireDefault(_useInterval);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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 = (0, _react.useRef)();
var _useWindowSize = (0, _useWindowSize3.default)(),
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 = (0, _react.useState)(true),
listen = _useState[0],
setListen = _useState[1];
(0, _react.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();
}
}
}
}
(0, _useInterval2.default)(function () {
listenBottomOffset();
},
// Stop interval when there is no next page.
hasNextPage ? checkInterval : 0);
return ref;
}
exports.default = useInfiniteScroll;
module.exports = exports["default"];