react-next-keep-alive
Version:
Module for caching views in next.js
301 lines (292 loc) • 11 kB
JavaScript
var React = require('react');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
function _extends() {
_extends = Object.assign ? Object.assign.bind() : 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);
}
var defaultEnabled = true;
var KeepAliveProvider = function KeepAliveProvider(props) {
var _componentData$type;
var children = props.children,
router = props.router;
var pageProps = children == null ? void 0 : children.props;
var componentData = React.cloneElement(children);
var CurrentComponent = componentData == null ? void 0 : componentData.type;
var keepAliveCache = React.useRef({});
var _useState = React.useState(),
forceUpdate = _useState[1];
var _ref = (componentData == null || (_componentData$type = componentData.type) == null ? void 0 : _componentData$type.keepAlive) || {},
keepAliveName = _ref.name,
keepScrollEnabled = _ref.keepScrollEnabled,
applyNewProps = _ref.applyNewProps;
// KeepAlive name
var name = typeof keepAliveName === 'function' ? keepAliveName({
props: pageProps,
router: router
}) : keepAliveName;
var isEnabled = function isEnabled() {
var _keepAliveCache$curre;
return keepAliveCache == null || (_keepAliveCache$curre = keepAliveCache.current) == null || (_keepAliveCache$curre = _keepAliveCache$curre[name]) == null ? void 0 : _keepAliveCache$curre.enabled;
};
var isKeptAlive = !!name;
var keepScroll = !!keepScrollEnabled;
// Add Component to retainedComponents if we haven't got it already
if (isKeptAlive && !keepAliveCache.current[name]) {
var Component = componentData == null ? void 0 : componentData.type;
var MemoComponent = React.memo(Component);
keepAliveCache.current[name] = {
Component: MemoComponent,
pageProps: pageProps,
scrollPos: 0,
name: name,
enabled: defaultEnabled
};
}
// Save the scroll position of current page before leaving
var handleRouteChangeStart = function handleRouteChangeStart() {
var _keepAliveCache$curre2;
if (isKeptAlive && keepAliveCache != null && (_keepAliveCache$curre2 = keepAliveCache.current) != null && _keepAliveCache$curre2[name]) {
keepAliveCache.current[name].scrollPos = window.scrollY;
}
};
// Restore the scroll position of cached page
var handleRouteChangeComplete = function handleRouteChangeComplete() {
if (isKeptAlive && isEnabled() && keepScroll) {
var _keepAliveCache$curre3;
window.scrollTo(0, ((_keepAliveCache$curre3 = keepAliveCache.current[name]) == null ? void 0 : _keepAliveCache$curre3.scrollPos) || 0);
// Just in case try again in next event loop
setTimeout(function () {
var _keepAliveCache$curre4;
window.scrollTo(0, ((_keepAliveCache$curre4 = keepAliveCache.current[name]) == null ? void 0 : _keepAliveCache$curre4.scrollPos) || 0);
}, 0);
}
};
// Enable/disable loading from cache
var handleLoadFromCache = function handleLoadFromCache(event) {
var _ref2 = (event == null ? void 0 : event.detail) || {},
controlsName = _ref2.name,
controlsEnabled = _ref2.enabled;
if (keepAliveCache.current[controlsName]) {
keepAliveCache.current[controlsName].enabled = controlsEnabled;
}
};
// Drop cache
var handleDropCache = function handleDropCache(event) {
var _ref3 = (event == null ? void 0 : event.detail) || {},
dropKeepAliveName = _ref3.name,
scrollToTop = _ref3.scrollToTop;
// If no name, drop all cache
if (!dropKeepAliveName) {
keepAliveCache.current = {};
} else if (typeof dropKeepAliveName === 'string') {
var _keepAliveCache$curre5;
(_keepAliveCache$curre5 = keepAliveCache.current) == null || delete _keepAliveCache$curre5[dropKeepAliveName];
} else if (typeof dropKeepAliveName === 'function') {
var _cachesToRemove$filte;
var caches = dropKeepAliveName == null ? void 0 : dropKeepAliveName(Object.keys(keepAliveCache.current));
var cachesToRemove = Array.isArray(caches) ? caches : [caches];
// eslint-disable-next-line no-unused-expressions
cachesToRemove == null || (_cachesToRemove$filte = cachesToRemove.filter(function (exists) {
return exists;
})) == null || _cachesToRemove$filte.forEach(function (cacheName) {
var _keepAliveCache$curre6;
return (_keepAliveCache$curre6 = keepAliveCache.current) == null || delete _keepAliveCache$curre6[cacheName];
});
}
if (scrollToTop && typeof window !== 'undefined') {
window.scrollTo(0, 0);
}
forceUpdate({});
};
// Handle scroll position caching - requires an up-to-date router.asPath
React.useEffect(function () {
router.events.on('routeChangeStart', handleRouteChangeStart);
router.events.on('routeChangeComplete', handleRouteChangeComplete);
return function () {
router.events.off('routeChangeStart', handleRouteChangeStart);
router.events.off('routeChangeComplete', handleRouteChangeComplete);
};
}, [router.asPath]);
// Emit mounting events
// @ts-ignore
React.useEffect(function () {
if (isKeptAlive) {
window.dispatchEvent(new CustomEvent('onKeepAliveMount', {
detail: name
}));
return function () {
window.dispatchEvent(new CustomEvent('onKeepAliveUnmount', {
detail: name
}));
};
}
}, [CurrentComponent, pageProps]);
/**
* Listen to changes (enabled/disabled)
*/
React.useEffect(function () {
window.addEventListener('keepAliveControls_LoadFromCache', handleLoadFromCache);
window.addEventListener('keepAliveControls_DropCache', handleDropCache);
return function () {
window.removeEventListener('keepAliveControls_LoadFromCache', handleLoadFromCache);
window.removeEventListener('keepAliveControls_DropCache', handleDropCache);
};
}, []);
var getCachedViewProps = function getCachedViewProps(cachedProps) {
// Apply new props
if (applyNewProps === true) {
return pageProps;
}
// Apply combination of old and new props
if (typeof applyNewProps === 'function') {
return applyNewProps(cachedProps, pageProps);
}
// Apply cached props
return cachedProps;
};
/**
* Custom useEffect which runs only when component alive.
*/
var getKeepAliveEffect = function getKeepAliveEffect(isHidden) {
var useKeepAliveEffect = function useKeepAliveEffect(effect, deps) {
return React.useEffect(function () {
if (!isHidden) {
return effect();
}
}, deps);
};
return useKeepAliveEffect;
};
return (
// eslint-disable-next-line react/jsx-fragments
React__default["default"].createElement(React.Fragment, null, (!isKeptAlive || !isEnabled()) && children, React__default["default"].createElement("div", {
style: {
display: isKeptAlive && isEnabled() ? 'block' : 'none'
},
id: "keep-alive-container",
"data-keepalivecontainer": true
}, Object.entries(keepAliveCache.current).map(function (_ref4) {
var cacheName = _ref4[0],
_ref4$ = _ref4[1],
Component = _ref4$.Component,
cachedProps = _ref4$.pageProps;
return React__default["default"].createElement("div", {
key: cacheName,
style: {
display: name === cacheName ? 'block' : 'none'
},
"data-keepalive": cacheName,
"data-keepalive-hidden": name !== cacheName
}, React__default["default"].createElement(Component, _extends({
isHiddenByKeepAlive: name !== cacheName,
useEffect: getKeepAliveEffect(name !== cacheName)
}, getCachedViewProps(cachedProps))));
})))
);
};
var defaultOpts = {
keepScrollEnabled: true,
applyNewProps: false
};
var withKeepAlive = function withKeepAlive(Component, name, opts) {
if (opts === void 0) {
opts = defaultOpts;
}
var KeepAlive = function KeepAlive(props) {
return (
// eslint-disable-next-line react/jsx-fragments
React__default["default"].createElement(React.Fragment, null, React__default["default"].createElement(Component, _extends({}, props)))
);
};
// Copy getInitial props so it will run as well
// @ts-ignore
if (Component.getInitialProps) {
// @ts-ignore
KeepAlive.getInitialProps = Component.getInitialProps;
}
var _ref = opts || {},
keepScrollEnabled = _ref.keepScrollEnabled,
applyNewProps = _ref.applyNewProps;
KeepAlive.keepAlive = {
name: name,
keepScrollEnabled: keepScrollEnabled,
applyNewProps: applyNewProps
};
return KeepAlive;
};
/**
* Hook for handling mount event
* @param name
* @param effect
*/
var useKeepAliveMountEffect = function useKeepAliveMountEffect(name, effect) {
// Trigger mount
var handleMountedEvent = function handleMountedEvent(e) {
if ((e == null ? void 0 : e.detail) === name && typeof effect === 'function') {
effect();
}
};
React.useEffect(function () {
window.addEventListener('onKeepAliveMount', handleMountedEvent);
return function () {
window.removeEventListener('onKeepAliveMount', handleMountedEvent);
};
}, []);
};
/**
* Hook for handling unmount event
* @param name
* @param effect
*/
var useKeepAliveUnmountEffect = function useKeepAliveUnmountEffect(name, effect) {
// Trigger mount
var handleUnmountedEvent = function handleUnmountedEvent(e) {
if ((e == null ? void 0 : e.detail) === name && typeof effect === 'function') {
effect();
}
};
React.useEffect(function () {
window.addEventListener('onKeepAliveUnmount', handleUnmountedEvent);
return function () {
window.removeEventListener('onKeepAliveUnmount', handleUnmountedEvent);
};
}, []);
};
function keepAliveLoadFromCache(name, enabled) {
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('keepAliveControls_LoadFromCache', {
detail: {
name: name,
enabled: enabled
}
}));
}
}
function keepAliveDropCache(name, scrollToTop) {
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('keepAliveControls_DropCache', {
detail: {
name: name,
scrollToTop: scrollToTop
}
}));
}
}
exports.KeepAliveProvider = KeepAliveProvider;
exports.keepAliveDropCache = keepAliveDropCache;
exports.keepAliveLoadFromCache = keepAliveLoadFromCache;
exports.useKeepAliveMountEffect = useKeepAliveMountEffect;
exports.useKeepAliveUnmountEffect = useKeepAliveUnmountEffect;
exports.withKeepAlive = withKeepAlive;
//# sourceMappingURL=index.js.map