commit c556508e24ee7a089b54c6389f3846c81198e8e2
parent 19077c4e6ada068eb8dae0d3094b2c845c4d0f4b
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Thu, 9 Jun 2016 22:35:14 +0300
Update history module to v3 (#692)
- Update `history` dependency to v3.0.0 ([changelog](https://github.com/ReactJSTraining/history/blob/master/CHANGES.md))
- Add `windowScrollX` and `windowScrollY` helpers to `core/DOMUtils`
- Rename `match()` to `UniversalRouter.resolve()`
- Fix scroll issues ([see article](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration))
Diffstat:
4 files changed, 60 insertions(+), 28 deletions(-)
diff --git a/package.json b/package.json
@@ -21,7 +21,7 @@
"front-matter": "2.0.8",
"graphiql": "0.7.1",
"graphql": "0.6.0",
- "history": "2.1.1",
+ "history": "3.0.0",
"isomorphic-style-loader": "1.0.0",
"jade": "1.11.0",
"jsonwebtoken": "7.0.0",
@@ -144,7 +144,15 @@
"extends": "stylelint-config-standard",
"rules": {
"string-quotes": "single",
- "selector-pseudo-class-no-unknown": [true, { "ignorePseudoClasses": ["global", "local"] }]
+ "selector-pseudo-class-no-unknown": [
+ true,
+ {
+ "ignorePseudoClasses": [
+ "global",
+ "local"
+ ]
+ }
+ ]
}
},
"scripts": {
diff --git a/src/client.js b/src/client.js
@@ -10,10 +10,16 @@
import 'babel-polyfill';
import ReactDOM from 'react-dom';
import FastClick from 'fastclick';
-import { match } from 'universal-router';
+import UniversalRouter from 'universal-router';
import routes from './routes';
import history from './core/history';
-import { addEventListener, removeEventListener } from './core/DOMUtils';
+import { readState, saveState } from 'history/lib/DOMStateStorage';
+import {
+ addEventListener,
+ removeEventListener,
+ windowScrollX,
+ windowScrollY,
+} from './core/DOMUtils';
const context = {
insertCss: styles => styles._insertCss(), // eslint-disable-line no-underscore-dangle
@@ -75,44 +81,52 @@ function render(container, state, component) {
}
function run() {
- let currentLocation = null;
const container = document.getElementById('app');
+ let currentLocation = history.getCurrentLocation();
// Make taps on links and buttons work fast on mobiles
FastClick.attach(document.body);
// Re-render the app when window.location changes
- const removeHistoryListener = history.listen(location => {
+ function onLocationChange(location) {
+ // Save the page scroll position into the current location's state
+ if (currentLocation.key) {
+ saveState(currentLocation.key, {
+ ...readState(currentLocation.key),
+ scrollX: windowScrollX(),
+ scrollY: windowScrollY(),
+ });
+ }
currentLocation = location;
- match(routes, {
+
+ UniversalRouter.resolve(routes, {
path: location.pathname,
query: location.query,
state: location.state,
context,
render: render.bind(undefined, container, location.state),
}).catch(err => console.error(err)); // eslint-disable-line no-console
- });
+ }
- // Save the page scroll position into the current location's state
- const supportPageOffset = window.pageXOffset !== undefined;
- const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
- const setPageOffset = () => {
- currentLocation.state = currentLocation.state || Object.create(null);
- if (supportPageOffset) {
- currentLocation.state.scrollX = window.pageXOffset;
- currentLocation.state.scrollY = window.pageYOffset;
- } else {
- currentLocation.state.scrollX = isCSS1Compat ?
- document.documentElement.scrollLeft : document.body.scrollLeft;
- currentLocation.state.scrollY = isCSS1Compat ?
- document.documentElement.scrollTop : document.body.scrollTop;
- }
- };
+ // Add History API listener and trigger initial change
+ const removeHistoryListener = history.listen(onLocationChange);
+ history.replace(currentLocation);
+
+ // https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
+ let originalScrollRestoration;
+ if (window.history && 'scrollRestoration' in window.history) {
+ originalScrollRestoration = window.history.scrollRestoration;
+ window.history.scrollRestoration = 'manual';
+ }
- addEventListener(window, 'scroll', setPageOffset);
- addEventListener(window, 'pagehide', () => {
- removeEventListener(window, 'scroll', setPageOffset);
+ // Prevent listeners collisions during history navigation
+ addEventListener(window, 'pagehide', function onPageHide() {
+ removeEventListener(window, 'pagehide', onPageHide);
removeHistoryListener();
+ if (originalScrollRestoration) {
+ window.history.scrollRestoration = originalScrollRestoration;
+ originalScrollRestoration = undefined;
+ }
});
}
diff --git a/src/core/DOMUtils.js b/src/core/DOMUtils.js
@@ -22,3 +22,13 @@ export function removeEventListener(node, event, listener) {
node.detachEvent(`on${event}`, listener);
}
}
+
+export function windowScrollX() {
+ return (window.pageXOffset !== undefined) ? window.pageXOffset :
+ (document.documentElement || document.body.parentNode || document.body).scrollLeft;
+}
+
+export function windowScrollY() {
+ return (window.pageYOffset !== undefined) ? window.pageYOffset :
+ (document.documentElement || document.body.parentNode || document.body).scrollTop;
+}
diff --git a/src/server.js b/src/server.js
@@ -16,7 +16,7 @@ import expressJwt from 'express-jwt';
import expressGraphQL from 'express-graphql';
import jwt from 'jsonwebtoken';
import ReactDOM from 'react-dom/server';
-import { match } from 'universal-router';
+import UniversalRouter from 'universal-router';
import PrettyError from 'pretty-error';
import passport from './core/passport';
import models from './data/models';
@@ -91,7 +91,7 @@ app.get('*', async (req, res, next) => {
data.trackingId = analytics.google.trackingId;
}
- await match(routes, {
+ await UniversalRouter.resolve(routes, {
path: req.path,
query: req.query,
context: {