react-view-router
Version:
react-view-router
514 lines • 21.9 kB
JavaScript
import "core-js/modules/es6.symbol.js";
import "core-js/modules/es7.object.get-own-property-descriptors.js";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["children"];
import _regeneratorRuntime from "@babel/runtime/regenerator";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import "core-js/modules/es6.regexp.constructor.js";
import "core-js/modules/es6.array.filter.js";
import "core-js/modules/es7.array.includes.js";
import React, { useState, useEffect } from 'react';
import { renderRoute, normalizeRoute, isFunction, isRouteChanged, isRoutesChanged, isPropChanged, nextTick, isMatchedRoutePropsChanged, getHostRouterView, warn, hasOwnProp, getRouteChildren, ignoreCatch } from './util';
import { computeRootMatch } from './match-path';
import { RouterContext, RouterViewContext } from './context';
import KeepAlive from './keep-alive';
function normalizeRouterViewProps(props) {
if (props.beforeEach && !hasOwnProp(props.beforeEach, 'global')) props.beforeEach.global = true;
if (props.afterEach && !hasOwnProp(props.afterEach, 'global')) props.afterEach.global = true;
}
export function _checkActivate(router, matchedRoute, event) {
if (!matchedRoute || !router) return;
var path1 = router.basenameNoSlash + matchedRoute.path;
var path2 = event.router.basenameNoSlash + event.target.path;
return path1 === path2;
}
export function _checkDeactivate(router, matchedRoute, event) {
if (!matchedRoute || !router) return;
var path1 = router.basenameNoSlash + matchedRoute.path;
var path2 = event.router.basenameNoSlash + event.target.path;
return path1.startsWith(path2);
}
class RouterView extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "_updateRef", ref => {
var _this$_kaRef;
var currentRoute = this.state.currentRoute;
if (currentRoute) currentRoute.componentInstances[this.name] = ref;
if (this.props && this.props._updateRef) this.props._updateRef(ref);
if ((_this$_kaRef = this._kaRef) !== null && _this$_kaRef !== void 0 && _this$_kaRef.activeNode) this._kaRef.activeNode.instance = ref;
// if (this._isMounted) this.setState({ currentRoute });
});
_defineProperty(this, "_updateKARef", ref => {
if (!ref && this._kaRef && !this.isNull(this.state.currentRoute)) {
ref = this._kaRef;
}
this._kaRef = ref;
});
_defineProperty(this, "_kaActivate", event => {
var _this$state = this.state,
parentRoute = _this$state.parentRoute,
router = _this$state.router;
if (_checkActivate(router, parentRoute, event)) {
this._isActivate = true;
this._refreshCurrentRoute();
}
});
_defineProperty(this, "_kaDeactivate", event => {
var _this$state2 = this.state,
parentRoute = _this$state2.parentRoute,
router = _this$state2.router;
if (_checkDeactivate(router, parentRoute, event)) {
this._events.deactivate.forEach(e => ignoreCatch(e)(event));
this._isActivate = false;
}
});
this.target = new.target;
this.isRouterViewInstance = true;
var _router = props && props.router;
var depth = props && props.depth ? Number(props.depth) : 0;
var state = {
_routerRoot: true,
parent: null,
depth,
inited: false,
resolving: false,
router: _router,
parentRoute: null,
currentRoute: null,
toRoute: null,
routes: [],
renderKeepAlive: false,
enableKeepAlive: false
};
this.state = state;
this._isMounted = false;
this._isActivate = true;
this._kaRef = null;
this._events = {
activate: [],
deactivate: []
};
normalizeRouterViewProps(props);
}
get name() {
var name = this.props.name;
if (!name) return 'default';
return name;
}
get currentRef() {
var currentRoute = this.state.currentRoute;
return currentRoute && currentRoute.componentInstances[this.name];
}
get isActivate() {
if (!this._isActivate) return false;
var _this$state3 = this.state,
_routerRoot = _this$state3._routerRoot,
router = _this$state3.router;
if (_routerRoot) {
if (router !== null && router !== void 0 && router.basename) {
var parent = getHostRouterView(this);
if (parent) return parent.isActivate;
}
}
return true;
}
_checkEnableKeepAlive() {
var key = 'keepAlive';
if (this._kaRef) return true;
if (hasOwnProp(this.props, key)) return true;
var _this$state4 = this.state,
currentRoute = _this$state4.currentRoute,
router = _this$state4.router;
if (hasOwnProp(currentRoute === null || currentRoute === void 0 ? void 0 : currentRoute.config, key)) return true;
var keepAliveProps = router === null || router === void 0 ? void 0 : router.options.keepAlive;
if (isFunction(keepAliveProps) || keepAliveProps instanceof RegExp) return true;
return false;
}
_filterRoutes(routes, state) {
var _this$props = this.props,
name = _this$props.name,
filter = _this$props.filter;
var ret = routes && routes.filter(r => {
var hasName = name && name !== 'default';
if (r.redirect || r.index) return hasName ? name === r.name : !r.name;
return hasName ? r.components && r.components[name] : r.component || r.components && r.components.default;
});
if (filter) ret = filter(ret, state || this.state);
return ret;
}
getMatchedRoute(route, depth = 0) {
var matched = (route === null || route === void 0 ? void 0 : route.matched) || [];
return matched.length > depth ? matched[depth] : null;
}
isKeepAliveRoute(currentRoute, toRoute, router) {
var _router2;
if (!router) router = this.state.router;
if (!currentRoute) return false;
var checkKeepAlive = v => {
if (isFunction(v)) v = v(currentRoute, toRoute, {
router: router,
view: this
});
return v instanceof RegExp ? toRoute ? v.test(toRoute.path) : false : isFunction(v) ? v : Boolean(v);
};
var keepAlive = currentRoute.config.keepAlive;
if (keepAlive) return checkKeepAlive(keepAlive);
keepAlive = this.props.keepAlive;
if (keepAlive) return checkKeepAlive(keepAlive);
keepAlive = (_router2 = router) === null || _router2 === void 0 ? void 0 : _router2.options.keepAlive;
return isFunction(keepAlive) ? checkKeepAlive(keepAlive) : false;
}
_refreshCurrentRoute(state, pendingState, callback) {
if (!state) state = this.state;
var router = state.router;
if (!router) throw new Error('state.router is null!');
var currentRoute = this.state.currentRoute;
var toRoute = this.getMatchedRoute(router.currentRoute, state.depth);
if (!toRoute) {
var _route = normalizeRoute({
path: ''
}, state.parentRoute && state.parentRoute.config);
toRoute = router.createMatchedRoute(_route, computeRootMatch());
router.currentRoute && router.currentRoute.matched.push(toRoute);
} else if (!toRoute || toRoute.redirect) toRoute = null;
var isChanged = isRouteChanged(currentRoute, toRoute);
var isMounted = this._isMounted;
var newState = {
enableKeepAlive: this.state.enableKeepAlive || this._checkEnableKeepAlive(),
currentRoute: toRoute
};
if (toRoute) toRoute.viewInstances[this.name] = this;
if (isMounted && isChanged && router.options.keepAlive) {
var _event = {
router,
source: this,
target: null,
to: toRoute,
from: currentRoute
};
newState.renderKeepAlive = this.isKeepAliveRoute(currentRoute, toRoute, router);
var kaRef = this._kaRef;
if (kaRef) {
var _toRoute2;
if (newState.renderKeepAlive) {
if ((currentRoute === null || currentRoute === void 0 ? void 0 : currentRoute.path) === kaRef.activeName) {
var _activeNode$instance;
_event.target = currentRoute;
this._events.deactivate.forEach(e => ignoreCatch(e)(_event));
var activeNode = kaRef.activeNode;
(activeNode === null || activeNode === void 0 || (_activeNode$instance = activeNode.instance) === null || _activeNode$instance === void 0 ? void 0 : _activeNode$instance.componentWillUnactivate) && ignoreCatch(activeNode.instance.componentWillUnactivate.bind(activeNode.instance))();
}
} else if (currentRoute) {
var _toRoute;
kaRef.remove(currentRoute.path, ((_toRoute = toRoute) === null || _toRoute === void 0 ? void 0 : _toRoute.path) === currentRoute.path);
}
var toPath = (_toRoute2 = toRoute) === null || _toRoute2 === void 0 ? void 0 : _toRoute2.path;
if (toPath && kaRef.activeName !== toPath) {
var toNode = kaRef.find(toPath);
if (toNode) {
var beforeActivate = toNode.beforeActivate || this.props.beforeActivate || router.options.beforeViewActivate;
if (!beforeActivate || beforeActivate(currentRoute, toRoute, {
view: this,
router
})) {
_event.target = toRoute;
nextTick(() => {
var _toNode$instance;
if (!this._isMounted) return;
if ((_toNode$instance = toNode.instance) !== null && _toNode$instance !== void 0 && _toNode$instance.componentDidActivate) ignoreCatch(toNode.instance.componentDidActivate.bind(toNode.instance))();
this._events.activate.forEach(e => ignoreCatch(e)(_event));
});
}
}
}
}
}
if (this.state.inited) {
if (pendingState) Object.assign(pendingState, newState);else if (isMounted) {
var currentRef = this.currentRef;
if (isChanged && !newState.renderKeepAlive && currentRef && router._isReactViewComponent(currentRef)) {
currentRef._willUnmount();
}
try {
this.setState(newState);
} catch (ex) {
console.error(ex);
}
var onRouteChange = this.props.onRouteChange;
if (isChanged && onRouteChange) ignoreCatch(onRouteChange)(toRoute, currentRoute);
}
} else if (state !== this.state) Object.assign(state, newState);
if (isMounted) callback && callback();
return toRoute;
}
_updateResolving(resolving, toRoute = null) {
if (!this._isMounted) return;
this.setState({
resolving,
toRoute
});
}
_resolveFallback() {
var ret = null;
var fallback = this.props.fallback;
if (isFunction(fallback)) {
var _this$state5 = this.state,
parentRoute = _this$state5.parentRoute,
currentRoute = _this$state5.currentRoute,
toRoute = _this$state5.toRoute,
inited = _this$state5.inited,
resolving = _this$state5.resolving,
depth = _this$state5.depth,
router = _this$state5.router;
ret = fallback({
parentRoute,
currentRoute,
toRoute,
inited,
resolving,
depth,
router,
view: this
});
} else if (/*#__PURE__*/React.isValidElement(fallback)) ret = fallback;
return ret || null;
}
isNull(route) {
return !route || !route.path || route.subpath === '' || route.isNull;
}
componentDidMount() {
var _this = this;
return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var _parent;
var state, router, parent, parentRouter, _parent$_events, _parent$_events2, pendingRoute, _ref, _ref2, location, _router3;
return _regeneratorRuntime.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_this._isMounted = true;
if (!_this.state.inited) {
_context.next = 1;
break;
}
return _context.abrupt("return");
case 1:
if (!(!_this._reactInternalFiber && !_this._reactInternals)) {
_context.next = 2;
break;
}
return _context.abrupt("return");
case 2:
state = _objectSpread({}, _this.state);
router = state.router;
parent = getHostRouterView(_this);
parentRouter = (_parent = parent) === null || _parent === void 0 ? void 0 : _parent.state.router;
if (router && parent) {
if (!parentRouter || router.mode !== parentRouter.mode || !router.basename) parent = null;
}
if (parent) {
state.parent = parent;
(_parent$_events = parent._events) === null || _parent$_events === void 0 || _parent$_events.activate.push(_this._kaActivate);
(_parent$_events2 = parent._events) === null || _parent$_events2 === void 0 || _parent$_events2.deactivate.unshift(_this._kaDeactivate);
}
if (!(router && (!parent || parentRouter !== router))) {
_context.next = 3;
break;
}
if (!router.isRunning) {
warn('[RouterView]warning: router is not running.');
}
router.viewRoot = _this;
pendingRoute = router.pendingRoute;
router.pendingRoute = null;
state.routes = _this._filterRoutes(router.routes);
_ref = router.history.getIndexAndLocation ? router.history.getIndexAndLocation() : [router.history.index, router.history.location], _ref2 = _slicedToArray(_ref, 2), location = _ref2[1];
router._handleRouteInterceptor(pendingRoute || _objectSpread({}, location), (ok, to) => {
if (!ok) return;
router && to && router.updateRoute(to);
_this._refreshCurrentRoute(state);
if (isFunction(ok)) ok(true, router.currentRoute);
if (_this._isMounted) _this.setState(Object.assign(state, {
inited: _this._isMounted
}));
}, true);
_context.next = 5;
break;
case 3:
state._routerRoot = false;
if (parent) {
_context.next = 4;
break;
}
throw new Error('[RouterView] cannot find root RouterView instance!');
case 4:
if (!router) router = state.router = parent.state.router;
state.depth = parent.state.depth + 1;
state.parentRoute = _this.getMatchedRoute((_router3 = router) === null || _router3 === void 0 ? void 0 : _router3.currentRoute, state.depth - 1);
state.routes = state.parentRoute ? _this._filterRoutes(getRouteChildren(state.parentRoute.config.children, state.parentRoute.config)) : [];
_this._refreshCurrentRoute(state);
if (_this._isMounted) _this.setState(Object.assign(state, {
inited: true
}));
case 5:
case "end":
return _context.stop();
}
}, _callee);
}))();
}
componentWillUnmount() {
this._isMounted = false;
var _this$state6 = this.state,
_routerRoot = _this$state6._routerRoot,
parent = _this$state6.parent,
router = _this$state6.router;
if (parent) {
var removeEvent = (name, fn) => {
if (!parent._events) return;
var events = parent._events[name];
var idx = events.indexOf(fn);
if (~idx) events.splice(idx, 1);
};
removeEvent('activate', this._kaActivate);
removeEvent('deactivate', this._kaDeactivate);
}
_routerRoot && router && (router.viewRoot = null);
}
shouldComponentUpdate(nextProps, nextState) {
if (!this._isMounted) return false;
if (this.state.resolving !== nextState.resolving) return true;
if (this.state.inited !== nextState.inited) return true;
if (this.state.depth !== nextState.depth) return true;
if (this.state.router !== nextState.router) return true;
var router = nextState.router;
if (isPropChanged(this.props, nextProps, (key, val, oldVal) => {
if (key === 'fallback' && nextState.resolving) return false;
if (key === 'keepAlive' && isFunction(val) && isFunction(oldVal)) return false;
if (['onRouteChange', 'beforeEach', 'afterEach', 'filter', 'beforeActivate'].includes(key)) return false;
return true;
})) return true;
if (isRouteChanged(this.state.currentRoute, nextState.currentRoute)) return true;
if (isRoutesChanged(this.state.routes, nextState.routes)) return true;
if (router && isMatchedRoutePropsChanged(this.state.currentRoute, router, this.name)) return true;
return false;
}
static getDerivedStateFromProps(nextProps) {
normalizeRouterViewProps(nextProps);
return null;
}
getComponentProps() {
var _this$props2 = this.props,
children = _this$props2.children,
props = _objectWithoutProperties(_this$props2, _excluded);
var excludeProps = this.target.defaultProps.excludeProps || RouterView.defaultProps.excludeProps || [];
excludeProps.forEach(key => delete props[key]);
return {
props,
children
};
}
getComponent(currentRoute) {
if (!currentRoute) return null;
var _this$state7 = this.state,
routes = _this$state7.routes,
router = _this$state7.router;
var _ref3 = router && router.currentRoute || {},
_ref3$query = _ref3.query,
query = _ref3$query === void 0 ? {} : _ref3$query,
_ref3$params = _ref3.params,
params = _ref3$params === void 0 ? {} : _ref3$params;
var _this$getComponentPro = this.getComponentProps(),
children = _this$getComponentPro.children,
props = _this$getComponentPro.props;
return renderRoute(currentRoute, routes, props, children, {
router,
name: this.name,
query,
params,
ref: this._updateRef
});
}
renderCurrent(currentRoute) {
if (this.isNull(currentRoute)) return this.props.children || null;
return this.getComponent(currentRoute);
}
renderContainer(current, currentRoute) {
var _this$state8 = this.state,
routes = _this$state8.routes,
router = _this$state8.router,
depth = _this$state8.depth;
var container = this.props.container;
if (router) {
container = router._callEvent('onViewContainer', container, {
routes,
route: currentRoute,
depth,
router,
view: this
}) || container;
}
return container && currentRoute ? container(current, currentRoute, current && current.props || this.getComponentProps(), this) : current;
}
render() {
if (!this.state.inited) return this._resolveFallback();
var router = this.state.router;
if (!router) return null;
var _this$state9 = this.state,
currentRoute = _this$state9.currentRoute,
_routerRoot = _this$state9._routerRoot,
resolving = _this$state9.resolving,
renderKeepAlive = _this$state9.renderKeepAlive,
enableKeepAlive = _this$state9.enableKeepAlive;
var renderUtils = router.options.renderUtils;
var ret = this.renderCurrent(currentRoute);
if (enableKeepAlive && renderUtils) {
var activeName = currentRoute ? currentRoute.path : '';
var extra = {};
if (isFunction(renderKeepAlive)) extra.beforeActivate = renderKeepAlive;
ret = /*#__PURE__*/React.createElement(KeepAlive, Object.assign({
utils: renderUtils,
activeName,
anchorName: `${router.mode}:${router.basenameNoSlash}:${activeName}`,
ref: this._updateKARef,
extra
}), ret);
}
ret = this.renderContainer(ret, currentRoute);
ret = /*#__PURE__*/React.createElement(RouterViewContext.Provider, {
value: this
}, ret);
if (_routerRoot) {
ret = /*#__PURE__*/React.createElement(RouterContext.Provider, {
value: router
}, ret);
}
ret = /*#__PURE__*/React.createElement(React.Fragment, {}, ret, resolving ? this._resolveFallback() : null);
return ret;
}
}
var RouterViewWrapper = /*#__PURE__*/React.forwardRef((props, ref) => {
var _useState = useState(!props.router || props.router.isRunning),
_useState2 = _slicedToArray(_useState, 2),
isRunning = _useState2[0],
setIsRunning = _useState2[1];
useEffect(() => {
if (!isRunning && props.router && props.router.isRunning) {
setIsRunning(true);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[isRunning, props.router && props.router.isRunning]);
return isRunning ? /*#__PURE__*/React.createElement(RouterView, _objectSpread(_objectSpread({}, props), {}, {
_updateRef: ref && (isFunction(ref) ? ref : r => ref.current = r)
})) : null;
});
RouterView.defaultProps = {
excludeProps: ['_updateRef', 'name', 'filter', 'fallback', 'container', 'router', 'depth', 'excludeProps', 'beforeEach', 'afterEach', 'onRouteChange', 'keepAlive']
};
export { RouterViewWrapper, RouterView as RouterViewComponent };
export default RouterViewWrapper;
//# sourceMappingURL=router-view.js.map