UNPKG

react-router

Version:

A complete routing library for React.js

336 lines (257 loc) • 11 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _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; }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _warning = require('warning'); var _warning2 = _interopRequireDefault(_warning); var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _AsyncUtils = require('./AsyncUtils'); var _RouteUtils = require('./RouteUtils'); var _RoutingUtils = require('./RoutingUtils'); var _PropTypes = require('./PropTypes'); var _RouterContextMixin = require('./RouterContextMixin'); var _RouterContextMixin2 = _interopRequireDefault(_RouterContextMixin); var _ScrollManagementMixin = require('./ScrollManagementMixin'); var _ScrollManagementMixin2 = _interopRequireDefault(_ScrollManagementMixin); var _Location = require('./Location'); var _Transition = require('./Transition'); var _Transition2 = _interopRequireDefault(_Transition); var _React$PropTypes = _react2['default'].PropTypes; var arrayOf = _React$PropTypes.arrayOf; var func = _React$PropTypes.func; var object = _React$PropTypes.object; function runTransition(prevState, routes, location, hooks, callback) { var transition = new _Transition2['default'](); (0, _RoutingUtils.getState)(routes, location, function (error, nextState) { if (error || nextState == null || transition.isCancelled) { callback(error, null, transition); } else { nextState.location = location; var transitionHooks = (0, _RoutingUtils.getTransitionHooks)(prevState, nextState); if (Array.isArray(hooks)) transitionHooks.unshift.apply(transitionHooks, hooks); (0, _AsyncUtils.loopAsync)(transitionHooks.length, function (index, next, done) { transitionHooks[index](nextState, transition, function (error) { if (error || transition.isCancelled) { done(error); // No need to continue. } else { next(); } }); }, function (error) { if (error || transition.isCancelled) { callback(error, null, transition); } else { (0, _RoutingUtils.getComponents)(nextState.branch, function (error, components) { if (error || transition.isCancelled) { callback(error, null, transition); } else { nextState.components = components; callback(null, nextState, transition); } }); } }); } }); } var Router = _react2['default'].createClass({ displayName: 'Router', mixins: [_RouterContextMixin2['default'], _ScrollManagementMixin2['default']], statics: { /** * Runs a transition to the given location using the given routes and * transition hooks (optional) and calls callback(error, state, transition) * when finished. This is primarily useful for server-side rendering. */ run: function run(routes, location, transitionHooks, callback) { if (typeof transitionHooks === 'function') { callback = transitionHooks; transitionHooks = null; } (0, _invariant2['default'])(typeof callback === 'function', 'Router.run needs a callback'); runTransition(null, routes, location, transitionHooks, callback); } }, propTypes: { createElement: func.isRequired, onAbort: func, onError: func, onUpdate: func, // Client-side history: _PropTypes.history, routes: _PropTypes.routes, // Routes may also be given as children (JSX) children: _PropTypes.routes, // Server-side location: _PropTypes.location, branch: _PropTypes.routes, params: object, components: arrayOf(_PropTypes.components) }, getDefaultProps: function getDefaultProps() { return { createElement: _react.createElement }; }, getInitialState: function getInitialState() { return { isTransitioning: false, location: null, branch: null, params: null, components: null }; }, _updateState: function _updateState(location) { var _this = this; (0, _invariant2['default'])((0, _Location.isLocation)(location), 'A <Router> needs a valid Location'); var hooks = this.transitionHooks; if (hooks) hooks = hooks.map(function (hook) { return (0, _RoutingUtils.createTransitionHook)(hook, _this); }); this.setState({ isTransitioning: true }); runTransition(this.state, this.routes, location, hooks, function (error, state, transition) { if (error) { _this.handleError(error); } else if (transition.isCancelled) { if (transition.redirectInfo) { var _transition$redirectInfo = transition.redirectInfo; var pathname = _transition$redirectInfo.pathname; var query = _transition$redirectInfo.query; var state = _transition$redirectInfo.state; _this.replaceWith(pathname, query, state); } else { (0, _invariant2['default'])(_this.state.location, 'You may not abort the initial transition'); _this.handleAbort(reason); } } else if (state == null) { (0, _warning2['default'])(false, 'Location "%s" did not match any routes', location.pathname); } else { _this.setState(state, _this.props.onUpdate); } _this.setState({ isTransitioning: false }); }); }, /** * Adds a transition hook that runs before all route hooks in a * transition. The signature is the same as route transition hooks. */ addTransitionHook: function addTransitionHook(hook) { if (!this.transitionHooks) this.transitionHooks = []; this.transitionHooks.push(hook); }, /** * Removes the given transition hook. */ removeTransitionHook: function removeTransitionHook(hook) { if (this.transitionHooks) this.transitionHooks = this.transitionHooks.filter(function (h) { return h !== hook; }); }, handleAbort: function handleAbort(reason) { if (this.props.onAbort) { this.props.onAbort.call(this, reason); } else { // The best we can do here is goBack so the location state reverts // to what it was. However, we also set a flag so that we know not // to run through _updateState again since state did not change. this._ignoreNextHistoryChange = true; this.goBack(); } }, handleError: function handleError(error) { if (this.props.onError) { this.props.onError.call(this, error); } else { // Throw errors by default so we don't silently swallow them! throw error; // This error probably originated in getChildRoutes or getComponents. } }, handleHistoryChange: function handleHistoryChange() { if (this._ignoreNextHistoryChange) { this._ignoreNextHistoryChange = false; } else { this._updateState(this.props.history.location); } }, componentWillMount: function componentWillMount() { var _props = this.props; var history = _props.history; var routes = _props.routes; var children = _props.children; var location = _props.location; var branch = _props.branch; var params = _props.params; var components = _props.components; if (history) { (0, _invariant2['default'])(routes || children, 'Client-side <Router>s need routes. Try using <Router routes> or ' + 'passing your routes as nested <Route> children'); this.routes = (0, _RouteUtils.createRoutes)(routes || children); if (typeof history.setup === 'function') history.setup(); // We need to listen first in case we redirect immediately. if (history.addChangeListener) history.addChangeListener(this.handleHistoryChange); this._updateState(history.location); } else { (0, _invariant2['default'])(location && branch && params && components, 'Server-side <Router>s need location, branch, params, and components ' + 'props. Try using Router.run to get all the props you need'); this.setState({ location: location, branch: branch, params: params, components: components }); } }, componentWillReceiveProps: function componentWillReceiveProps(nextProps) { (0, _invariant2['default'])(this.props.history === nextProps.history, '<Router history> may not be changed'); if (nextProps.history) { var currentRoutes = this.props.routes || this.props.children; var nextRoutes = nextProps.routes || nextProps.children; if (currentRoutes !== nextRoutes) { this.routes = (0, _RouteUtils.createRoutes)(nextRoutes); // Call this here because _updateState // uses this.routes to determine state. if (nextProps.history.location) this._updateState(nextProps.history.location); } } }, componentWillUnmount: function componentWillUnmount() { var history = this.props.history; if (history && history.removeChangeListener) history.removeChangeListener(this.handleHistoryChange); }, _createElement: function _createElement(component, props) { return typeof component === 'function' ? this.props.createElement(component, props) : null; }, render: function render() { var _this2 = this; var _state = this.state; var location = _state.location; var branch = _state.branch; var params = _state.params; var components = _state.components; var isTransitioning = _state.isTransitioning; var element = null; if (components) { element = components.reduceRight(function (element, components, index) { if (components == null) return element; // Don't create new children; use the grandchildren. var route = branch[index]; var routeParams = (0, _RoutingUtils.getRouteParams)(route, params); var props = { location: location, params: params, route: route, routeParams: routeParams, isTransitioning: isTransitioning }; if ((0, _react.isValidElement)(element)) { props.children = element; } else if (element) { // In render, do var { header, sidebar } = this.props; _extends(props, element); } if (typeof components === 'object') { var elements = {}; for (var key in components) if (components.hasOwnProperty(key)) elements[key] = _this2._createElement(components[key], props); return elements; } return _this2._createElement(components, props); }, element); } (0, _invariant2['default'])(element === null || element === false || (0, _react.isValidElement)(element), 'The root route must render a single element'); return element; } }); exports['default'] = Router; module.exports = exports['default'];