UNPKG

rrr-lazy

Version:

Lazy load component with react && react-router.

473 lines (374 loc) 13.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var isDeepEqual = _interopDefault(require('react-fast-compare')); var createContext = _interopDefault(require('create-react-context')); var React = _interopDefault(require('react')); var PropTypes = _interopDefault(require('prop-types')); var hoistNonReactStatics = _interopDefault(require('hoist-non-react-statics')); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } var _createContext = createContext(), LazyProvider = _createContext.Provider, LazyConsumer = _createContext.Consumer; var IntersectionListener = /*#__PURE__*/ function () { function IntersectionListener(options) { var _this = this; this.callbacks = new WeakMap(); this.observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { var callback = _this.callbacks.get(entry.target); if (entry.target && callback) { callback(entry, _this.observer); } else { _this.observer.unobserve(entry.target); } }); }, options); } var _proto = IntersectionListener.prototype; _proto.listen = function listen(element, callback) { var _this2 = this; this.callbacks.set(element, callback); this.observer.observe(element); return function () { _this2.callbacks.delete(element); _this2.observer.unobserve(element); }; }; return IntersectionListener; }(); var observerPool = new WeakMap(); function createIntersectionListener(options) { var _options$root = options.root, root = _options$root === void 0 ? typeof window === 'undefined' ? Object : window : _options$root, _options$rootMargin = options.rootMargin, rootMargin = _options$rootMargin === void 0 ? '' : _options$rootMargin, _options$threshold = options.threshold, threshold = _options$threshold === void 0 ? '' : _options$threshold; if (!observerPool.get(root)) { observerPool.set(root, {}); } var group = observerPool.get(root); var key = rootMargin + "_" + threshold; var result = group[key]; if (!result) { result = new IntersectionListener(options); group[key] = result; } return result; } var _Lazy$propTypes, _Lazy$defaultProps; var Status = { Unload: 'unload', Loading: 'loading', Loaded: 'loaded' }; var VERSION_PROP = '@@rrr-lazy/Version'; var Lazy = /*#__PURE__*/ function (_React$Component) { _inheritsLoose(Lazy, _React$Component); function Lazy(props) { var _this; _this = _React$Component.call(this, props) || this; _this.startListen = _this.startListen.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.stopListen = _this.stopListen.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.enterViewport = _this.enterViewport.bind(_assertThisInitialized(_assertThisInitialized(_this))); var version = props[VERSION_PROP]; _this.state = { version: version, // eslint-disable-line react/no-unused-state status: Status.Unload }; return _this; } Lazy.getDerivedStateFromProps = function getDerivedStateFromProps(props, state) { var version = props[VERSION_PROP]; if (version !== state.version) { return { version: version, status: Status.Unload }; } return {}; }; var _proto = Lazy.prototype; _proto.componentDidMount = function componentDidMount() { this.startListen(); }; _proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) { return !isDeepEqual(this.props, nextProps) || !isDeepEqual(this.state, nextState); }; _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) { var status = this.state.status; var onUnload = this.props.onUnload; if (status !== prevState.status && status === Status.Unload) { if (onUnload) { onUnload(); } this.startListen(); } }; _proto.componentWillUnmount = function componentWillUnmount() { this.stopListen(); if (this.node) { this.node = null; } }; _proto.startListen = function startListen() { var _this2 = this; this.stopListen(); var _this$props = this.props, root = _this$props.root, rootMargin = _this$props.rootMargin; var opts = {}; if (root) { opts.root = typeof root === 'string' ? document.querySelector(root) : root; } if (rootMargin) { opts.rootMargin = rootMargin; } var intersectionListener = createIntersectionListener(opts); if (this.node && !this.unlisten) { this.unlisten = intersectionListener.listen(this.node, function (entry) { if (entry.isIntersecting || entry.intersectionRatio > 0) { _this2.stopListen(); _this2.enterViewport(); } }); } }; _proto.stopListen = function stopListen() { if (this.unlisten) { this.unlisten(); this.unlisten = null; } }; _proto.enterViewport = function enterViewport() { var _this3 = this; var status = this.state.status; var _this$props2 = this.props, onLoading = _this$props2.onLoading, onLoaded = _this$props2.onLoaded, onError = _this$props2.onError; if (!this.node || status !== Status.Unload) { return null; } return Promise.resolve().then(function () { if (!_this3.node) throw new Error('ABORT'); _this3.setState({ status: Status.Loading }); if (onLoading) { return onLoading(); } return null; }).then(function () { if (!_this3.node) throw new Error('ABORT'); _this3.setState({ status: Status.Loaded }); if (onLoaded) { return onLoaded(); } return null; }).catch(function (error) { if (error.message !== 'ABORT') { if (onError) { return onError(error); } throw error; } return null; }); }; _proto.render = function render() { var _this4 = this; var _this$props3 = this.props, root = _this$props3.root, rootMargin = _this$props3.rootMargin, render = _this$props3.render, loaderComponent = _this$props3.loaderComponent, _this$props3$loaderPr = _this$props3.loaderProps, loaderProps = _this$props3$loaderPr === void 0 ? {} : _this$props3$loaderPr, onLoaded = _this$props3.onLoaded, onLoading = _this$props3.onLoading, onUnload = _this$props3.onUnload, onError = _this$props3.onError, restProps = _objectWithoutPropertiesLoose(_this$props3, ["root", "rootMargin", "render", "loaderComponent", "loaderProps", "onLoaded", "onLoading", "onUnload", "onError"]); var status = this.state.status; if (status !== Status.Loaded) { return React.createElement(loaderComponent, _objectSpread({}, loaderProps, { ref: function ref(node) { _this4.node = node; } }), render(status, restProps)); } return render(status, restProps); }; return Lazy; }(React.Component); Lazy.propTypes = (_Lazy$propTypes = {}, _Lazy$propTypes[VERSION_PROP] = PropTypes.any, _Lazy$propTypes.render = PropTypes.func.isRequired, _Lazy$propTypes.root = PropTypes.oneOfType([PropTypes.string].concat(typeof HTMLElement === 'undefined' ? [] : PropTypes.instanceOf(HTMLElement))), _Lazy$propTypes.rootMargin = PropTypes.string, _Lazy$propTypes.loaderComponent = PropTypes.string, _Lazy$propTypes.loaderProps = PropTypes.object, _Lazy$propTypes.onError = PropTypes.func, _Lazy$propTypes.onLoaded = PropTypes.func, _Lazy$propTypes.onLoading = PropTypes.func, _Lazy$propTypes.onUnload = PropTypes.func, _Lazy$propTypes); Lazy.defaultProps = (_Lazy$defaultProps = {}, _Lazy$defaultProps[VERSION_PROP] = null, _Lazy$defaultProps.root = null, _Lazy$defaultProps.rootMargin = null, _Lazy$defaultProps.loaderComponent = 'div', _Lazy$defaultProps.loaderProps = null, _Lazy$defaultProps.onError = null, _Lazy$defaultProps.onLoaded = null, _Lazy$defaultProps.onLoading = null, _Lazy$defaultProps.onUnload = null, _Lazy$defaultProps); function LazyWrapper(props) { return React.createElement(LazyConsumer, null, function (version) { var _objectSpread2; var passProps = _objectSpread({}, props, (_objectSpread2 = {}, _objectSpread2[VERSION_PROP] = version, _objectSpread2)); return React.createElement(Lazy, passProps); }); } function interopRequireDefault(obj) { // eslint-disable-next-line no-underscore-dangle return obj && obj.__esModule ? obj : { default: obj }; } function noop() {} function Empty() { return null; } var LazyDecorated = /*#__PURE__*/ function (_React$PureComponent) { _inheritsLoose(LazyDecorated, _React$PureComponent); function LazyDecorated(props) { var _this; _this = _React$PureComponent.call(this, props) || this; _this.handleLoading = _this.handleLoading.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.renderComponent = _this.renderComponent.bind(_assertThisInitialized(_assertThisInitialized(_this))); _this.state = { Component: null }; return _this; } var _proto = LazyDecorated.prototype; _proto.handleLoading = function handleLoading() { var _this2 = this; var _this$props = this.props, getComponent = _this$props.getComponent, lazyProps = _this$props.lazyProps; var _lazyProps$onLoading = lazyProps.onLoading, onLoading = _lazyProps$onLoading === void 0 ? noop : _lazyProps$onLoading; return Promise.resolve().then(function () { return getComponent(); }).then(function (c) { var Component = interopRequireDefault(c).default || Empty; _this2.setState({ Component: Component }); return onLoading(); }); }; _proto.renderComponent = function renderComponent(status, props) { var Component = this.state.Component; var lazyProps = this.props.lazyProps; var render = lazyProps.render; if (typeof render === 'function') { return render(status, props, Component); } if (!Component || status !== 'loaded') return null; return React.createElement(Component, props); }; _proto.render = function render() { var _this$props2 = this.props, lazyProps = _this$props2.lazyProps, ownProps = _this$props2.ownProps; return React.createElement(LazyWrapper, _extends({}, ownProps, lazyProps, { render: this.renderComponent, onLoading: this.handleLoading })); }; return LazyDecorated; }(React.PureComponent); LazyDecorated.propTypes = { getComponent: PropTypes.func.isRequired, lazyProps: PropTypes.object.isRequired, ownProps: PropTypes.object.isRequired }; var lazy = function lazy(opts) { return function (WrappedComponent) { var getComponent = opts.getComponent, lazyProps = _objectWithoutPropertiesLoose(opts, ["getComponent"]); var Component = function Component(ownProps) { return React.createElement(LazyDecorated, { getComponent: getComponent === 'function' ? getComponent : function () { return WrappedComponent; }, lazyProps: lazyProps, ownProps: ownProps }); }; Component.WrappedComponent = WrappedComponent; hoistNonReactStatics(Component, WrappedComponent); return Component; }; }; exports.Lazy = LazyWrapper; exports.lazy = lazy; exports.LazyProvider = LazyProvider;