StaticRouter.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import React from "react";
  2. import PropTypes from "prop-types";
  3. import { createLocation, createPath } from "history";
  4. import invariant from "tiny-invariant";
  5. import warning from "tiny-warning";
  6. import Router from "./Router.js";
  7. function addLeadingSlash(path) {
  8. return path.charAt(0) === "/" ? path : "/" + path;
  9. }
  10. function addBasename(basename, location) {
  11. if (!basename) return location;
  12. return {
  13. ...location,
  14. pathname: addLeadingSlash(basename) + location.pathname
  15. };
  16. }
  17. function stripBasename(basename, location) {
  18. if (!basename) return location;
  19. const base = addLeadingSlash(basename);
  20. if (location.pathname.indexOf(base) !== 0) return location;
  21. return {
  22. ...location,
  23. pathname: location.pathname.substr(base.length)
  24. };
  25. }
  26. function createURL(location) {
  27. return typeof location === "string" ? location : createPath(location);
  28. }
  29. function staticHandler(methodName) {
  30. return () => {
  31. invariant(false, "You cannot %s with <StaticRouter>", methodName);
  32. };
  33. }
  34. function noop() {}
  35. /**
  36. * The public top-level API for a "static" <Router>, so-called because it
  37. * can't actually change the current location. Instead, it just records
  38. * location changes in a context object. Useful mainly in testing and
  39. * server-rendering scenarios.
  40. */
  41. class StaticRouter extends React.Component {
  42. navigateTo(location, action) {
  43. const { basename = "", context = {} } = this.props;
  44. context.action = action;
  45. context.location = addBasename(basename, createLocation(location));
  46. context.url = createURL(context.location);
  47. }
  48. handlePush = location => this.navigateTo(location, "PUSH");
  49. handleReplace = location => this.navigateTo(location, "REPLACE");
  50. handleListen = () => noop;
  51. handleBlock = () => noop;
  52. render() {
  53. const { basename = "", context = {}, location = "/", ...rest } = this.props;
  54. const history = {
  55. createHref: path => addLeadingSlash(basename + createURL(path)),
  56. action: "POP",
  57. location: stripBasename(basename, createLocation(location)),
  58. push: this.handlePush,
  59. replace: this.handleReplace,
  60. go: staticHandler("go"),
  61. goBack: staticHandler("goBack"),
  62. goForward: staticHandler("goForward"),
  63. listen: this.handleListen,
  64. block: this.handleBlock
  65. };
  66. return <Router {...rest} history={history} staticContext={context} />;
  67. }
  68. }
  69. if (__DEV__) {
  70. StaticRouter.propTypes = {
  71. basename: PropTypes.string,
  72. context: PropTypes.object,
  73. location: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
  74. };
  75. StaticRouter.prototype.componentDidMount = function() {
  76. warning(
  77. !this.props.history,
  78. "<StaticRouter> ignores the history prop. To use a custom history, " +
  79. "use `import { Router }` instead of `import { StaticRouter as Router }`."
  80. );
  81. };
  82. }
  83. export default StaticRouter;