Skip to content

Commit 64f0f17

Browse files
committed
Record scroll position in dispatch
Recording scroll position in dispatch method is necessary for clients using alternative batching strategies, such as react-raf-batching[1]. With an alternative batching strategy, by the time componentWillUpdate is called, browser may have tried to restore position itself (not always correctly), and the recorded position is thus wrong. Also, don't record position for REPLACE action since there is no way to get back to it. [1]: https://github.com/petehunt/react-raf-batching
1 parent 7e9aead commit 64f0f17

File tree

2 files changed

+30
-19
lines changed

2 files changed

+30
-19
lines changed

modules/mixins/Scrolling.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,38 +32,43 @@ function shouldUpdateScroll(state, prevState) {
3232
*/
3333
var Scrolling = {
3434

35+
statics: {
36+
/**
37+
* Records curent scroll position as the last known position for the given URL path.
38+
*/
39+
recordScrollPosition: function (path) {
40+
if (!this.scrollHistory)
41+
this.scrollHistory = {};
42+
43+
this.scrollHistory[path] = getWindowScrollPosition();
44+
},
45+
46+
/**
47+
* Returns the last known scroll position for the given URL path.
48+
*/
49+
getScrollPosition: function (path) {
50+
if (!this.scrollHistory)
51+
this.scrollHistory = {};
52+
53+
return this.scrollHistory[path] || null;
54+
}
55+
},
56+
3557
componentWillMount: function () {
3658
invariant(
3759
this.getScrollBehavior() == null || canUseDOM,
3860
'Cannot use scroll behavior without a DOM'
3961
);
40-
41-
this._scrollHistory = {};
4262
},
4363

4464
componentDidMount: function () {
4565
this._updateScroll();
4666
},
4767

48-
componentWillUpdate: function () {
49-
this._scrollHistory[this.state.path] = getWindowScrollPosition();
50-
},
51-
5268
componentDidUpdate: function (prevProps, prevState) {
5369
this._updateScroll(prevState);
5470
},
5571

56-
componentWillUnmount: function () {
57-
delete this._scrollHistory;
58-
},
59-
60-
/**
61-
* Returns the last known scroll position for the given URL path.
62-
*/
63-
getScrollPosition: function (path) {
64-
return this._scrollHistory[path] || null;
65-
},
66-
6772
_updateScroll: function (prevState) {
6873
if (!shouldUpdateScroll(this.state, prevState)) {
6974
return;
@@ -73,7 +78,7 @@ var Scrolling = {
7378

7479
if (scrollBehavior)
7580
scrollBehavior.updateScrollPosition(
76-
this.getScrollPosition(this.state.path),
81+
this.constructor.getScrollPosition(this.state.path),
7782
this.state.action
7883
);
7984
}

modules/utils/createRouter.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var invariant = require('react/lib/invariant');
44
var canUseDOM = require('react/lib/ExecutionEnvironment').canUseDOM;
55
var ImitateBrowserBehavior = require('../behaviors/ImitateBrowserBehavior');
66
var RouteHandler = require('../components/RouteHandler');
7+
var LocationActions = require('../actions/LocationActions');
78
var HashLocation = require('../locations/HashLocation');
89
var HistoryLocation = require('../locations/HistoryLocation');
910
var RefreshLocation = require('../locations/RefreshLocation');
@@ -264,9 +265,14 @@ function createRouter(options) {
264265
* hooks wait, the transition is fully synchronous.
265266
*/
266267
dispatch: function (path, action, callback) {
267-
if (state.path === path)
268+
var prevPath = state.path;
269+
if (prevPath === path)
268270
return; // Nothing to do!
269271

272+
if (prevPath && action !== LocationActions.REPLACE) {
273+
this.recordScrollPosition(prevPath);
274+
}
275+
270276
var match = this.match(path);
271277

272278
warning(

0 commit comments

Comments
 (0)