Skip to content

Commit fabcecc

Browse files
benoit-tremblaytimdorr
authored andcommitted
Explicit error on <Route> outside <Router> (remix-run#4939)
* Explicit error on <Route> outside <Router> When using <Route> outside a valid <Router>, the message is not that explicit. It throws: `Cannot read property 'route' of undefined` Even though it's not that bad, I propose we make it even more explicit. Can't be bad to know where the error is coming from. If you made a typo when importing the router, it's not obvious otherwise. This will throw an error rather than a warning because the library code would crash otherwise. * Add unit test * Fix unit test change * Add router check before first usage on <Router /> * Switch throw to invariant for <Router> check * Add <Router> check on <Link>, <Switch>, <Redirect>, <Prompt>
1 parent e1e994d commit fabcecc

File tree

9 files changed

+75
-5
lines changed

9 files changed

+75
-5
lines changed

packages/react-router-dom/modules/Link.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
3+
import invariant from 'invariant'
34

45
const isModifiedEvent = (event) =>
56
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey)
@@ -58,6 +59,11 @@ class Link extends React.Component {
5859
render() {
5960
const { replace, to, innerRef, ...props } = this.props // eslint-disable-line no-unused-vars
6061

62+
invariant(
63+
this.context.router,
64+
'You should not use <Link> outside a valid <Router>'
65+
)
66+
6167
const href = this.context.router.history.createHref(
6268
typeof to === 'string' ? { pathname: to } : to
6369
)

packages/react-router-dom/modules/__tests__/Link-test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ describe('A <Link>', () => {
2525
expect(href).toEqual('/the/path?the=query#the-hash')
2626
})
2727

28+
it('crashes explicitly with no valid <Router>', () => {
29+
const node = document.createElement('div')
30+
31+
expect(() => {
32+
ReactDOM.render((
33+
<Link to="/">link</Link>
34+
), node)
35+
}).toThrow(/You should not use <Link> outside a valid <Router>/)
36+
2837
it('exposes its ref via an innerRef prop', done => {
2938
const node = document.createElement('div')
3039

packages/react-router-dom/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"history": "^4.5.1",
44+
"invariant": "^2.2.2",
4445
"loose-envify": "^1.3.1",
4546
"prop-types": "^15.5.4",
4647
"react-router": "^4.1.1"

packages/react-router/modules/Prompt.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
3+
import invariant from 'invariant'
34

45
/**
56
* The public API for prompting the user before navigating away
@@ -41,6 +42,11 @@ class Prompt extends React.Component {
4142
}
4243

4344
componentWillMount() {
45+
invariant(
46+
this.context.router,
47+
'You should not use <Prompt> outside a valid <Router>'
48+
)
49+
4450
if (this.props.when)
4551
this.enable(this.props.message)
4652
}

packages/react-router/modules/Redirect.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
3+
import invariant from 'invariant'
34

45
/**
56
* The public API for updating the location programatically
@@ -34,6 +35,11 @@ class Redirect extends React.Component {
3435
}
3536

3637
componentWillMount() {
38+
invariant(
39+
this.context.router,
40+
'You should not use <Redirect> outside a valid <Router>'
41+
)
42+
3743
if (this.isStatic())
3844
this.perform()
3945
}

packages/react-router/modules/Route.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import warning from 'warning'
2+
import invariant from 'invariant';
23
import React from 'react'
34
import PropTypes from 'prop-types'
45
import matchPath from './matchPath'
@@ -49,10 +50,16 @@ class Route extends React.Component {
4950
match: this.computeMatch(this.props, this.context.router)
5051
}
5152

52-
computeMatch({ computedMatch, location, path, strict, exact }, { route }) {
53+
computeMatch({ computedMatch, location, path, strict, exact }, router) {
5354
if (computedMatch)
5455
return computedMatch // <Switch> already computed the match for us
5556

57+
invariant(
58+
router,
59+
'You should not use <Route> or withRouter() outside a valid <Router>'
60+
)
61+
62+
const { route } = router
5663
const pathname = (location || route.location).pathname
5764

5865
return path ? matchPath(pathname, { path, strict, exact }) : route.match
@@ -63,17 +70,17 @@ class Route extends React.Component {
6370

6471
warning(
6572
!(component && render),
66-
'You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored'
73+
'You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored'
6774
)
6875

6976
warning(
7077
!(component && children),
71-
'You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored'
78+
'You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored'
7279
)
7380

7481
warning(
7582
!(render && children),
76-
'You should not use <Route render> and <Route children> in the same route; <Route children> will be ignored'
83+
'You should not use <Route render> and <Route children> in the same route; <Route children> will be ignored'
7784
)
7885
}
7986

packages/react-router/modules/Switch.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react'
22
import PropTypes from 'prop-types'
33
import warning from 'warning'
4+
import invariant from 'invariant'
45
import matchPath from './matchPath'
56

67
/**
@@ -18,6 +19,13 @@ class Switch extends React.Component {
1819
location: PropTypes.object
1920
}
2021

22+
componentWillMount() {
23+
invariant(
24+
this.context.router,
25+
'You should not use <Switch> outside a valid <Router>'
26+
)
27+
}
28+
2129
componentWillReceiveProps(nextProps) {
2230
warning(
2331
!(nextProps.location && !this.props.location),

packages/react-router/modules/__tests__/Route-test.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ describe('A <Route>', () => {
8888
push('/sushi/spicy-tuna')
8989
expect(node.innerHTML).toContain('/sushi/spicy-tuna')
9090
})
91+
92+
it('crash explicitly with no valid <Router>', () => {
93+
const node = document.createElement('div')
94+
95+
expect(() => {
96+
ReactDOM.render((
97+
<Route path="/" render={() => null} />
98+
), node)
99+
}).toThrow(/You should not use <Route> or withRouter\(\) outside a valid <Router>/)
100+
})
91101
})
92102

93103
describe('A <Route> with dynamic segments in the path', () => {
@@ -342,7 +352,7 @@ describe('A <Route location>', () => {
342352

343353
expect(node.innerHTML).toContain(TEXT)
344354
})
345-
355+
346356
it('continues to use parent\'s prop location after navigation', () => {
347357
const TEXT = 'cheddar pretzel'
348358
const node = document.createElement('div')

packages/react-router/modules/__tests__/Switch-test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ describe('A <Switch>', () => {
135135

136136
expect(node.innerHTML).toMatch(/one/)
137137
})
138+
139+
it('crash explicitly with no valid <Router>', () => {
140+
const node = document.createElement('div')
141+
142+
expect(() => {
143+
ReactDOM.render((
144+
<Switch>
145+
<Route path="/one" render={() => (
146+
<h1>one</h1>
147+
)}/>
148+
<Route path="/two" render={() => (
149+
<h1>two</h1>
150+
)}/>
151+
</Switch>
152+
), node)
153+
}).toThrow(/You should not use <Switch> outside a valid <Router>/)
154+
})
138155
})
139156

140157

0 commit comments

Comments
 (0)