@times-components/lazy-load
Version:
Use IntersectionObserver to lazy load resources
137 lines (115 loc) • 3.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _propTypes = _interopRequireDefault(require("prop-types"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* eslint-env browser */
class LazyLoad extends _react.Component {
constructor(props) {
super(props);
this.isObserving = false;
this.pending = new Set();
this.pendingTimer = null;
this.state = {
clientHasRendered: false,
nodes: new Map()
};
this.unobserved = new Set();
this.unobservedTimer = null;
this.registerNode = this.registerNode.bind(this);
if (typeof window === "undefined" || !("IntersectionObserver" in window)) {
return;
}
this.isObserving = true;
const options = {
rootMargin: props.rootMargin,
threshold: props.threshold
};
this.observer = new window.IntersectionObserver(this.handleObservation.bind(this), options);
}
componentDidMount() {
const newState = {
clientHasRendered: true
}; // eslint-disable-next-line react/no-did-mount-set-state
this.setState(newState);
}
componentWillUnmount() {
if (this.observer) {
this.observer.disconnect();
}
clearTimeout(this.pendingTimer);
clearTimeout(this.unobservedTimer);
this.pending.clear();
this.unobserved.clear();
}
handleObservation(entries) {
const threshold = this.props.threshold;
const nodes = this.state.nodes;
entries.forEach(_ref => {
let target = _ref.target,
intersectionRatio = _ref.intersectionRatio;
if (intersectionRatio >= threshold && !nodes.get(target.id)) {
this.pending.add(target);
} else if (intersectionRatio < threshold && this.pending.has(target)) {
this.pending.delete(target);
}
});
clearTimeout(this.pendingTimer);
if (this.pending.size) {
this.pendingTimer = setTimeout(() => {
if (!this.pending.size) {
return;
}
this.setState(state => ({
nodes: new Map([...state.nodes, ...[...this.pending].map(n => [n.id, n])])
}));
this.pending.clear();
}, 100);
}
}
registerNode(node) {
if (!node) {
return;
}
if (!this.observer) {
if (this.unobserved.has(node)) {
return;
}
this.unobserved.add(node);
clearTimeout(this.unobservedTimer);
this.unobservedTimer = setTimeout(() => {
this.setState({
nodes: new Map([...this.unobserved].map(n => [n.id, n]))
});
this.unobserved.clear();
}, 10);
return;
}
this.observer.observe(node);
}
render() {
const children = this.props.children;
const _this$state = this.state,
clientHasRendered = _this$state.clientHasRendered,
nodes = _this$state.nodes;
return children({
clientHasRendered,
isObserving: this.isObserving,
observed: nodes,
registerNode: this.registerNode
});
}
}
LazyLoad.propTypes = {
children: _propTypes.default.func.isRequired,
rootMargin: _propTypes.default.string,
threshold: _propTypes.default.number.isRequired
};
LazyLoad.defaultProps = {
rootMargin: "0px"
};
var _default = LazyLoad;
exports.default = _default;