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";
|
8
|
import matchPath from "./matchPath";
|
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
|
* The public API for matching a single path and rendering.
|
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 // <Switch> already computed the match for us
|
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
|
// Preact uses an empty array as children by
|
49
|
// default, so use null if that's the case.
|
50
|
if (Array.isArray(children) && children.length === 0) {
|
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;
|