UNPKG

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