Router.js 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import React from "react";
  2. import PropTypes from "prop-types";
  3. import warning from "tiny-warning";
  4. import HistoryContext from "./HistoryContext.js";
  5. import RouterContext from "./RouterContext.js";
  6. /**
  7. * The public API for putting history on context.
  8. */
  9. class Router extends React.Component {
  10. static computeRootMatch(pathname) {
  11. return { path: "/", url: "/", params: {}, isExact: pathname === "/" };
  12. }
  13. constructor(props) {
  14. super(props);
  15. this.state = {
  16. location: props.history.location
  17. };
  18. // This is a bit of a hack. We have to start listening for location
  19. // changes here in the constructor in case there are any <Redirect>s
  20. // on the initial render. If there are, they will replace/push when
  21. // they mount and since cDM fires in children before parents, we may
  22. // get a new location before the <Router> is mounted.
  23. this._isMounted = false;
  24. this._pendingLocation = null;
  25. if (!props.staticContext) {
  26. this.unlisten = props.history.listen(location => {
  27. if (this._isMounted) {
  28. this.setState({ location });
  29. } else {
  30. this._pendingLocation = location;
  31. }
  32. });
  33. }
  34. }
  35. componentDidMount() {
  36. this._isMounted = true;
  37. if (this._pendingLocation) {
  38. this.setState({ location: this._pendingLocation });
  39. }
  40. }
  41. componentWillUnmount() {
  42. if (this.unlisten) {
  43. this.unlisten();
  44. this._isMounted = false;
  45. this._pendingLocation = null;
  46. }
  47. }
  48. render() {
  49. return (
  50. <RouterContext.Provider
  51. value={{
  52. history: this.props.history,
  53. location: this.state.location,
  54. match: Router.computeRootMatch(this.state.location.pathname),
  55. staticContext: this.props.staticContext
  56. }}
  57. >
  58. <HistoryContext.Provider
  59. children={this.props.children || null}
  60. value={this.props.history}
  61. />
  62. </RouterContext.Provider>
  63. );
  64. }
  65. }
  66. if (__DEV__) {
  67. Router.propTypes = {
  68. children: PropTypes.node,
  69. history: PropTypes.object.isRequired,
  70. staticContext: PropTypes.object
  71. };
  72. Router.prototype.componentDidUpdate = function(prevProps) {
  73. warning(
  74. prevProps.history === this.props.history,
  75. "You cannot change <Router history>"
  76. );
  77. };
  78. }
  79. export default Router;