Skip to content

Commit 2a85b74

Browse files
committed
[changed] handler keys to be optional
gimme some of that sweet, sweet DOM diffing closes #97
1 parent 5ff2985 commit 2a85b74

File tree

7 files changed

+114
-22
lines changed

7 files changed

+114
-22
lines changed

UPGRADE_GUIDE.md

+45
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,51 @@ To see discussion around these API changes, please refer to the
55
[changelog](/CHANGELOG.md) and git log the commits to find the issues
66
they refer to.
77

8+
0.5.x -> 0.6.x
9+
--------------
10+
11+
If you have dynamic segments and are depending on `getInitialState`,
12+
`componentWillMount`, or `componentDidMount` to fire between transitions
13+
to the same route--like `users/123` and `users/456`, then you have two
14+
options: add `addHandlerKey={true}` to your route and keep the previous
15+
behavior (but lose out on performance), or implement
16+
`componentWillReceiveProps`.
17+
18+
```js
19+
// 0.5.x
20+
<Route handler={User} path="/user/:userId"/>
21+
22+
// 0.6.x
23+
<Route handler={User} path="/user/:userId" addHandlerKey={true} />
24+
25+
// 0.5.x
26+
var User = React.createClass({
27+
getInitialState: function() {
28+
return {
29+
user: getUser(this.props.params.userId);
30+
}
31+
}
32+
});
33+
34+
// 0.6.x
35+
var User = React.createClass({
36+
getInitialState: function() {
37+
return this.getState();{
38+
},
39+
40+
componentWillReceiveProps: function(newProps) {
41+
this.setState(this.getState(newProps));
42+
},
43+
44+
getState: function(props) {
45+
props = props || this.props;
46+
return {
47+
user: getUser(props.params.userId)
48+
};
49+
}
50+
});
51+
```
52+
853
0.4.x -> 0.5.x
954
--------------
1055

docs/api/components/Route.md

+20
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ inherit the path of their parent.
2323

2424
The component to be rendered when the route is active.
2525

26+
### `addHandlerKey`
27+
28+
Defaults to `false`.
29+
30+
If you have dynamic segments in your URL, a transition from `/users/123`
31+
to `/users/456` does not call `getInitialState`, `componentWillMount` or
32+
`componentWillUnmount`. If you are using those lifecycle hooks to fetch
33+
data and set state, you will also need to implement
34+
`componentWillReceiveProps` on your handler, just like any other
35+
component with changing props. This way, you can leverage the
36+
performance of the React DOM diff algorithm. Look at the `Contact`
37+
handler in the `master-detail` example.
38+
39+
If you'd rather be lazy, set this to `true` and the router will add a
40+
key to your route, causing all new DOM to be built, and then the life
41+
cycle hooks will all be called.
42+
43+
You will want this to be `true` if you're doing animations with React's
44+
TransitionGroup component.
45+
2646
### `preserveScrollPosition`
2747

2848
If `true`, the router will not scroll the window up when the route is

docs/guides/overview.md

+17
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,22 @@ how you can turn this parameter into state on your component. Or for a
289289
more basic approach, make an ajax call in `componentDidMount` with the
290290
value.
291291

292+
Important Note About Dynamic Segments
293+
-------------------------------------
294+
295+
If you have dynamic segments in your URL, a transition from `/users/123`
296+
to `/users/456` does not call `getInitialState`, `componentWillMount` or
297+
`componentWillUnmount`. If you are using those lifecycle hooks to fetch
298+
data and set state, you will also need to implement
299+
`componentWillReceiveProps` on your handler, just like any other
300+
component whose props are changing. This way you can leverage the
301+
performance of the React DOM diff algorithm. Look at the `Contact`
302+
handler in the `master-detail` example.
303+
304+
If you'd rather be lazy, you can use the `addHandlerKey` option and set
305+
it to `true` on your route to opt-out of the performance. See also
306+
[Route][Route].
307+
292308
Links
293309
-----
294310

@@ -306,5 +322,6 @@ it has to offer. Check out the [API Docs][API] to learn about
306322
redirecting transitions, query parameters and more.
307323

308324
[AsyncState]:../api/mixins/AsyncState.md
325+
[Route]:../api/components/Route.md
309326
[API]:../api/
310327

examples/animations/app.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var Image = React.createClass({
3636
var routes = (
3737
<Routes>
3838
<Route handler={App}>
39-
<Route name="image" path="/:service" handler={Image}/>
39+
<Route name="image" path="/:service" handler={Image} addHandlerKey={true} />
4040
</Route>
4141
</Routes>
4242
);

examples/master-detail/app.js

+27-19
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,17 @@ var Index = React.createClass({
130130
});
131131

132132
var Contact = React.createClass({
133-
getInitialState: function() {
133+
getStateFromStore: function(props) {
134+
props = props || this.props;
134135
return {
135-
contact: ContactStore.getContact(this.props.params.id)
136+
contact: ContactStore.getContact(props.params.id)
136137
};
137138
},
138139

140+
getInitialState: function() {
141+
return this.getStateFromStore();
142+
},
143+
139144
componentDidMount: function() {
140145
ContactStore.addChangeListener(this.updateContact);
141146
},
@@ -144,13 +149,15 @@ var Contact = React.createClass({
144149
ContactStore.removeChangeListener(this.updateContact);
145150
},
146151

152+
componentWillReceiveProps: function(newProps) {
153+
this.setState(this.getStateFromStore(newProps));
154+
},
155+
147156
updateContact: function () {
148157
if (!this.isMounted())
149158
return;
150159

151-
this.setState({
152-
contact: ContactStore.getContact(this.props.params.id)
153-
});
160+
this.setState(this.getStateFromStore())
154161
},
155162

156163
destroy: function() {
@@ -204,20 +211,6 @@ var NotFound = React.createClass({
204211
}
205212
});
206213

207-
var routes = (
208-
<Route handler={App}>
209-
<DefaultRoute handler={Index}/>
210-
<Route name="new" path="contact/new" handler={NewContact}/>
211-
<Route name="not-found" path="contact/not-found" handler={NotFound}/>
212-
<Route name="contact" path="contact/:id" handler={Contact}/>
213-
</Route>
214-
);
215-
216-
React.renderComponent(
217-
<Routes children={routes}/>,
218-
document.getElementById('example')
219-
);
220-
221214
// Request utils.
222215

223216
function getJSON(url, cb) {
@@ -249,3 +242,18 @@ function deleteJSON(url, cb) {
249242
req.open('DELETE', url);
250243
req.send();
251244
}
245+
246+
var routes = (
247+
<Route handler={App}>
248+
<DefaultRoute handler={Index}/>
249+
<Route name="new" path="contact/new" handler={NewContact}/>
250+
<Route name="not-found" path="contact/not-found" handler={NotFound}/>
251+
<Route name="contact" path="contact/:id" handler={Contact}/>
252+
</Route>
253+
);
254+
255+
React.renderComponent(
256+
<Routes children={routes}/>,
257+
document.getElementById('example')
258+
);
259+

examples/simple-master-detail/app.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ var State = React.createClass({
5353
var routes = (
5454
<Routes>
5555
<Route handler={App}>
56-
<Route name="state" path="state/:abbr" handler={State}/>
56+
<Route name="state" path="state/:abbr" addHandlerKey={true} handler={State}/>
5757
</Route>
5858
</Routes>
5959
);

modules/components/Routes.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,12 @@ function computeHandlerProps(matches, query) {
411411
props = Route.getUnreservedProps(route.props);
412412

413413
props.ref = REF_NAME;
414-
props.key = Path.injectParams(route.props.path, match.params);
415414
props.params = match.params;
416415
props.query = query;
417416

417+
if (route.props.addHandlerKey)
418+
props.key = Path.injectParams(route.props.path, match.params);
419+
418420
if (childHandler) {
419421
props.activeRouteHandler = childHandler;
420422
} else {

0 commit comments

Comments
 (0)