1 | import React from "react";
|
2 | import { isValidElementType } from "react-is";
|
3 | import PropTypes from "prop-types";
|
4 | import invariant from "tiny-invariant";
|
5 | import warning from "tiny-warning";
|
6 |
|
7 | import RouterContext from "./RouterContext.js";
|
8 | import matchPath from "./matchPath.js";
|
9 |
|
10 | function isEmptyChildren(children) {
|
11 | return React.Children.count(children) === 0;
|
12 | }
|
13 |
|
14 | function evalChildrenDev(children, props, path) {
|
15 | const value = children(props);
|
16 |
|
17 | warning(
|
18 | value !== undefined,
|
19 | "You returned `undefined` from the `children` function of " +
|
20 | `<Route${path ? ` path="${path}"` : ""}>, but you ` +
|
21 | "should have returned a React element or `null`"
|
22 | );
|
23 |
|
24 | return value || null;
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | class Route extends React.Component {
|
31 | render() {
|
32 | return (
|
33 | <RouterContext.Consumer>
|
34 | {context => {
|
35 | invariant(context, "You should not use <Route> outside a <Router>");
|
36 |
|
37 | const location = this.props.location || context.location;
|
38 | const match = this.props.computedMatch
|
39 | ? this.props.computedMatch
|
40 | : this.props.path
|
41 | ? matchPath(location.pathname, this.props)
|
42 | : context.match;
|
43 |
|
44 | const props = { ...context, location, match };
|
45 |
|
46 | let { children, component, render } = this.props;
|
47 |
|
48 |
|
49 |
|
50 | if (Array.isArray(children) && isEmptyChildren(children)) {
|
51 | children = null;
|
52 | }
|
53 |
|
54 | return (
|
55 | <RouterContext.Provider value={props}>
|
56 | {props.match
|
57 | ? children
|
58 | ? typeof children === "function"
|
59 | ? __DEV__
|
60 | ? evalChildrenDev(children, props, this.props.path)
|
61 | : children(props)
|
62 | : children
|
63 | : component
|
64 | ? React.createElement(component, props)
|
65 | : render
|
66 | ? render(props)
|
67 | : null
|
68 | : typeof children === "function"
|
69 | ? __DEV__
|
70 | ? evalChildrenDev(children, props, this.props.path)
|
71 | : children(props)
|
72 | : null}
|
73 | </RouterContext.Provider>
|
74 | );
|
75 | }}
|
76 | </RouterContext.Consumer>
|
77 | );
|
78 | }
|
79 | }
|
80 |
|
81 | if (__DEV__) {
|
82 | Route.propTypes = {
|
83 | children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
84 | component: (props, propName) => {
|
85 | if (props[propName] && !isValidElementType(props[propName])) {
|
86 | return new Error(
|
87 | `Invalid prop 'component' supplied to 'Route': the prop is not a valid React component`
|
88 | );
|
89 | }
|
90 | },
|
91 | exact: PropTypes.bool,
|
92 | location: PropTypes.object,
|
93 | path: PropTypes.oneOfType([
|
94 | PropTypes.string,
|
95 | PropTypes.arrayOf(PropTypes.string)
|
96 | ]),
|
97 | render: PropTypes.func,
|
98 | sensitive: PropTypes.bool,
|
99 | strict: PropTypes.bool
|
100 | };
|
101 |
|
102 | Route.prototype.componentDidMount = function() {
|
103 | warning(
|
104 | !(
|
105 | this.props.children &&
|
106 | !isEmptyChildren(this.props.children) &&
|
107 | this.props.component
|
108 | ),
|
109 | "You should not use <Route component> and <Route children> in the same route; <Route component> will be ignored"
|
110 | );
|
111 |
|
112 | warning(
|
113 | !(
|
114 | this.props.children &&
|
115 | !isEmptyChildren(this.props.children) &&
|
116 | this.props.render
|
117 | ),
|
118 | "You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored"
|
119 | );
|
120 |
|
121 | warning(
|
122 | !(this.props.component && this.props.render),
|
123 | "You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored"
|
124 | );
|
125 | };
|
126 |
|
127 | Route.prototype.componentDidUpdate = function(prevProps) {
|
128 | warning(
|
129 | !(this.props.location && !prevProps.location),
|
130 | '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'
|
131 | );
|
132 |
|
133 | warning(
|
134 | !(!this.props.location && prevProps.location),
|
135 | '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'
|
136 | );
|
137 | };
|
138 | }
|
139 |
|
140 | export default Route;
|