From 47a6329048cce4c23e1f7f5072fd42e8bb4a695c Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Wed, 4 Nov 2015 15:28:04 -0500 Subject: [PATCH] Fix scroll position history enhancers --- modules/useScrollToTopBehavior.js | 10 ++--- modules/useStandardScrollBehavior.js | 62 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 modules/useStandardScrollBehavior.js diff --git a/modules/useScrollToTopBehavior.js b/modules/useScrollToTopBehavior.js index 2accfca3b6..46ed755408 100644 --- a/modules/useScrollToTopBehavior.js +++ b/modules/useScrollToTopBehavior.js @@ -4,12 +4,10 @@ export default function useScrollToTopBehavior(createHistory) { return function (options) { const history = createHistory(options) - history.listen(function (location) { - if (location.action === 'POP') { - return - } - - setWindowScrollPosition(0, 0) + history.listen(() => { + // Need to defer this to after other listeners fire in case some of them + // update the page. + setTimeout(() => setWindowScrollPosition(0, 0)) }) return history diff --git a/modules/useStandardScrollBehavior.js b/modules/useStandardScrollBehavior.js new file mode 100644 index 0000000000..14e5fc41c9 --- /dev/null +++ b/modules/useStandardScrollBehavior.js @@ -0,0 +1,62 @@ +import { readState, saveState } from 'history/lib/DOMStateStorage' +import { addEventListener, getWindowScrollPosition, setWindowScrollPosition } + from './DOMUtils' + +export default function useStandardScrollBehavior(createHistory) { + return function (options) { + const history = createHistory(options) + + let currentLocation + let savePositionHandle = null + + addEventListener(window, 'scroll', () => { + if (savePositionHandle !== null) { + clearTimeout(savePositionHandle) + } + + savePositionHandle = setTimeout(() => { + savePositionHandle = null + + if (!currentLocation) { + return + } + const { key } = currentLocation + + const state = readState(key) + saveState(key, { + ...state, scrollPosition: getWindowScrollPosition() + }) + }) + }) + + history.listenBefore(() => { + if (savePositionHandle !== null) { + clearTimeout(savePositionHandle) + savePositionHandle = null + } + }) + + function getScrollPosition() { + const state = readState(currentLocation.key) + if (!state) { + return null + } + + return state.scrollPosition + } + + history.listen(location => { + currentLocation = location + + const scrollPosition = getScrollPosition() || {} + const { x = 0, y = 0 } = scrollPosition + + // Need to defer the scroll operation because this listener fires before + // e.g. the router updates its state, and this might need to scroll past + // the end of the page pre-transition if the popped page was longer. + setTimeout(() => setWindowScrollPosition(x, y)) + }) + + return history + } +}