react-router-redial
Version:
Easy integration of redial for React Router
404 lines (339 loc) • 13.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _triggerHooks = require('./triggerHooks');
var _triggerHooks2 = _interopRequireDefault(_triggerHooks);
var _createMap = require('./createMap');
var _createMap2 = _interopRequireDefault(_createMap);
var _mapKeys = require('./util/mapKeys');
var _mapKeys2 = _interopRequireDefault(_mapKeys);
var _getAllComponents = require('./getAllComponents');
var _getAllComponents2 = _interopRequireDefault(_getAllComponents);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* global __REDIAL_PROPS__ */
function hydrate(renderProps) {
if (typeof __REDIAL_PROPS__ !== 'undefined' && Array.isArray(__REDIAL_PROPS__)) {
var getMapKeyForComponent = (0, _mapKeys2.default)(renderProps.routes, renderProps.components);
var components = (0, _getAllComponents2.default)(renderProps.components);
var componentKeys = components.map(getMapKeyForComponent);
return (0, _createMap2.default)(componentKeys, __REDIAL_PROPS__);
}
return (0, _createMap2.default)();
}
var RedialContext = function (_Component) {
_inherits(RedialContext, _Component);
function RedialContext(props, context) {
_classCallCheck(this, RedialContext);
var _this = _possibleConstructorReturn(this, (RedialContext.__proto__ || Object.getPrototypeOf(RedialContext)).call(this, props, context));
_this.state = {
loading: false,
afterTransitionLoading: false,
aborted: function aborted() {
return false;
},
abort: function abort() {},
prevRenderProps: undefined,
redialMap: props.redialMap || hydrate(props.renderProps),
initial: props.beforeTransition.length > 0
};
_this.completed = {
beforeTransition: false,
afterTransition: false,
error: null
};
return _this;
}
_createClass(RedialContext, [{
key: 'getChildContext',
value: function getChildContext() {
var _this2 = this;
var _state = this.state,
loading = _state.loading,
afterTransitionLoading = _state.afterTransitionLoading,
redialMap = _state.redialMap;
return {
redialContext: {
loading: loading,
afterTransitionLoading: afterTransitionLoading,
redialMap: redialMap,
reloadComponent: function reloadComponent(component) {
_this2.reloadComponent(component);
},
abortLoading: function abortLoading() {
_this2.abort();
}
}
};
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.load(this.props.renderProps.components, this.props.renderProps);
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (nextProps.renderProps.location === this.props.renderProps.location) {
return;
}
this.load(nextProps.renderProps.components, nextProps.renderProps);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
this.unmounted = true;
}
}, {
key: 'reloadComponent',
value: function reloadComponent(component) {
this.load(component, this.props.renderProps, true);
}
}, {
key: 'abort',
value: function abort(becauseError, _abort) {
// Make sure we only cancel if it is the correct ongoing request
if (!_abort || this.state.abort === _abort) {
// We need to be in a loading state for it to make sense
// to abort something
if (this.state.loading || this.state.afterTransitionLoading) {
this.state.abort();
this.setState({
loading: false,
afterTransitionLoading: false
});
if (this.props.onAborted) {
this.props.onAborted(becauseError);
}
}
}
}
}, {
key: 'load',
value: function load(components, renderProps) {
var _this3 = this;
var force = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var isAborted = false;
var _abort2 = function _abort2() {
isAborted = true;
};
var aborted = function aborted() {
return isAborted;
};
var bail = function bail() {
var currentLocation = _this3.props.renderProps.location;
if (aborted()) {
return 'aborted';
} else if (currentLocation !== renderProps.location) {
return 'location-changed';
}
return false;
};
if (this.props.onStarted) {
this.props.onStarted(force);
}
this.completed.beforeTransition = false;
this.setState({
aborted: aborted,
abort: _abort2,
loading: true,
prevRenderProps: this.state.loading || this.state.aborted() ? this.state.prevRenderProps : this.props.renderProps
});
this.runBeforeTransition(this.props.beforeTransition, components, renderProps, force, bail).catch(function (error) {
var afterTransition = false;
if (error && error.afterTransition !== undefined) {
afterTransition = error.afterTransition;
error = error.error; // eslint-disable-line
}
_this3.props.onError(error, {
reason: bail() || 'other',
// If not defined before it's a beforeTransition error
beforeTransition: !afterTransition,
router: _this3.props.renderProps.router,
abort: function abort() {
return _this3.abort(true, _abort2);
}
});
});
if (this.props.parallel) {
this.runAfterTransition(this.props.afterTransition, components, renderProps, force, bail).then(function () {
if (_this3.completed.afterTransition) {
_this3.completed.afterTransition = false;
_this3.props.onCompleted('afterTransition');
}
}).catch(function (err) {
// We will only propagate this error if beforeTransition have been completed
// This because the beforeTransition error is more critical
var error = function error() {
return _this3.props.onError(err, {
reason: bail() || 'other',
beforeTransition: false,
router: _this3.props.renderProps.router,
abort: function abort() {
return _this3.abort(true, _abort2);
}
});
};
if (_this3.completed.beforeTransition) {
error();
} else {
_this3.completed.error = error;
}
});
}
}
}, {
key: 'runAfterTransition',
value: function runAfterTransition(hooks, components, renderProps) {
var _this4 = this;
var force = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var bail = arguments[4];
// Get afterTransition data, will not block route transitions
this.completed.afterTransition = false;
this.completed.error = null;
this.setState({
afterTransitionLoading: true
});
return (0, _triggerHooks2.default)({
hooks: hooks,
components: components,
renderProps: renderProps,
redialMap: this.state.redialMap,
locals: this.props.locals,
force: force,
bail: bail
}).then(function (_ref) {
var redialMap = _ref.redialMap;
_this4.completed.afterTransition = true;
_this4.setState({
afterTransitionLoading: false,
redialMap: redialMap
});
});
}
}, {
key: 'runBeforeTransition',
value: function runBeforeTransition(hooks, components, renderProps) {
var _this5 = this;
var force = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var bail = arguments[4];
var completeRouteTransition = function completeRouteTransition(redialMap) {
if (!bail() && !_this5.unmounted) {
_this5.setState({
loading: false,
redialMap: redialMap,
prevRenderProps: undefined,
initial: false
});
_this5.props.onCompleted('beforeTransition');
_this5.completed.beforeTransition = true;
// Start afterTransition if we are not in parallel
if (!_this5.props.parallel) {
return _this5.runAfterTransition(_this5.props.afterTransition, components, renderProps, force, bail).then(function () {
_this5.props.onCompleted('afterTransition');
}).catch(function (error) {
return Promise.reject({ error: error, afterTransition: true });
});
} else if (_this5.completed.afterTransition) {
_this5.completed.afterTransition = false;
_this5.props.onCompleted('afterTransition');
} else if (_this5.completed.error) {
_this5.completed.error();
}
}
return Promise.resolve();
};
return (0, _triggerHooks2.default)({
hooks: hooks,
components: components,
renderProps: renderProps,
redialMap: this.state.redialMap,
locals: this.props.locals,
force: force,
bail: bail
}).then(function (_ref2) {
var redialMap = _ref2.redialMap;
return completeRouteTransition(redialMap);
});
}
}, {
key: 'render',
value: function render() {
if (this.props.initialLoading && this.state.initial && this.state.redialMap.size() === 0) {
return this.props.initialLoading();
}
var props = (this.state.loading || this.state.aborted()) && this.state.prevRenderProps;
if (props) {
/* eslint-disable no-unused-vars */
// Omit `createElement`. Otherwise we might skip `renderRouteContext` in `applyMiddleware`.
var createElement = props.createElement,
prevProps = _objectWithoutProperties(props, ['createElement']);
/* eslint-enable no-unused-vars */
return _react2.default.cloneElement(this.props.children, prevProps);
}
return this.props.children;
}
}]);
return RedialContext;
}(_react.Component);
RedialContext.displayName = 'RedialContext';
RedialContext.propTypes = {
children: _propTypes2.default.node.isRequired,
// RouterContext default
renderProps: _propTypes2.default.object.isRequired,
// Custom
locals: _propTypes2.default.object,
beforeTransition: _propTypes2.default.array,
afterTransition: _propTypes2.default.array,
parallel: _propTypes2.default.bool,
initialLoading: _propTypes2.default.func,
onError: _propTypes2.default.func,
onAborted: _propTypes2.default.func,
onStarted: _propTypes2.default.func,
onCompleted: _propTypes2.default.func,
// Server
redialMap: _propTypes2.default.object
};
RedialContext.defaultProps = {
beforeTransition: [],
afterTransition: [],
parallel: false,
onError: function onError(err, _ref3) {
var beforeTransition = _ref3.beforeTransition;
if (process.env.NODE_ENV !== 'production') {
var type = beforeTransition ? 'beforeTransition' : 'afterTransition';
console.error(type, err);
}
},
onAborted: function onAborted(becauseError) {
if (process.env.NODE_ENV !== 'production') {
if (becauseError) {
console.warn('Loading was aborted from an error');
} else {
console.warn('Loading was aborted manually');
}
}
},
onStarted: function onStarted(force) {
if (process.env.NODE_ENV !== 'production') {
console.info('Loading started. Force:', force);
}
},
onCompleted: function onCompleted(type) {
if (process.env.NODE_ENV !== 'production') {
console.info('Loading completed. Type:', type);
}
}
};
RedialContext.childContextTypes = {
redialContext: _propTypes2.default.object
};
exports.default = RedialContext;