react-infinite-scroll-hook
Version:
A simple hook to create infinite scroll components
324 lines (268 loc) • 10.9 kB
JavaScript
/*!
* react-infinite-scroll-hook v2.0.0 - https://onderonur.github.io/react-infinite-scroll-hook/
* MIT Licensed
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("react"));
else if(typeof define === 'function' && define.amd)
define(["react"], factory);
else if(typeof exports === 'object')
exports["ReactInfiniteScrollHook"] = factory(require("react"));
else
root["ReactInfiniteScrollHook"] = factory(root["React"]);
})(window, function(__WEBPACK_EXTERNAL_MODULE__0__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE__0__;
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(2);
/***/ }),
/* 2 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// EXTERNAL MODULE: external {"root":"React","commonjs2":"react","commonjs":"react","amd":"react"}
var external_root_React_commonjs2_react_commonjs_react_amd_react_ = __webpack_require__(0);
// CONCATENATED MODULE: ./src/useWindowSize.js
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function useWindowSize() {
var validWindow = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object";
var getSize = Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useCallback"])(function () {
var size = {
width: validWindow ? window.innerWidth : undefined,
height: validWindow ? window.innerHeight : undefined
};
return size;
}, [validWindow]);
var _useState = Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useState"])(getSize()),
size = _useState[0],
setSize = _useState[1];
Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useEffect"])(function () {
function handleResize() {
setSize(getSize());
}
if (validWindow) {
window.addEventListener("resize", handleResize);
return function () {
window.removeEventListener("resize", handleResize);
};
}
}, [getSize, validWindow]);
return size;
}
/* harmony default export */ var src_useWindowSize = (useWindowSize);
// CONCATENATED MODULE: ./src/useInterval.js
function useInterval(callback, delay) {
var savedCallback = Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useRef"])();
Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useEffect"])(function () {
savedCallback.current = callback;
}, [callback]);
Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useEffect"])(function () {
function tick() {
savedCallback.current();
}
if (delay) {
var id = setInterval(function () {
tick();
}, delay);
return function () {
return clearInterval(id);
};
}
}, [delay]);
}
/* harmony default export */ var src_useInterval = (useInterval);
// CONCATENATED MODULE: ./src/useInfiniteScroll.js
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 = Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useRef"])();
var _useWindowSize = src_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 = Object(external_root_React_commonjs2_react_commonjs_react_amd_react_["useState"])(true),
listen = _useState[0],
setListen = _useState[1];
Object(external_root_React_commonjs2_react_commonjs_react_amd_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();
}
}
}
}
src_useInterval(function () {
listenBottomOffset();
},
// Stop interval when there is no next page.
hasNextPage ? checkInterval : 0);
return ref;
}
/* harmony default export */ var src_useInfiniteScroll = (useInfiniteScroll);
// CONCATENATED MODULE: ./src/index.js
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "useInfiniteScroll", function() { return src_useInfiniteScroll; });
/***/ })
/******/ ])["default"];
});