From ed81a2774fe7f1728beeda94c27cd7accf67f543 Mon Sep 17 00:00:00 2001 From: tim neutkens Date: Sat, 28 Jan 2017 14:53:07 +0100 Subject: [PATCH 1/6] Remember scroll position on error --- client/index.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/client/index.js b/client/index.js index 626fee5d0a806..4fd63e9c94c4a 100644 --- a/client/index.js +++ b/client/index.js @@ -22,6 +22,7 @@ const { const Component = evalScript(component).default const ErrorComponent = evalScript(errorComponent).default let lastAppProps +let lastScroll export const router = createRouter(pathname, query, { Component, @@ -65,10 +66,27 @@ async function doRender ({ Component, props, err }) { props = await loadGetInitialProps(Component, { err, pathname, query }) } + if (Component === ErrorComponent) { + lastScroll = { + x: window.pageXOffset, + y: window.pageYOffset + } + } + Component = Component || lastAppProps.Component props = props || lastAppProps.props const appProps = { Component, props, err, router, headManager } - lastAppProps = appProps ReactDOM.render(createElement(App, appProps), container) + + if (lastScroll && + Component !== ErrorComponent && + lastAppProps.Component === ErrorComponent) { + // Restore scroll after ErrorComponent was replaced with a page component by HMR + const {x, y} = lastScroll + window.scroll(x, y) + lastScroll = false + } + + lastAppProps = appProps } From 7441832d2ffa824c1a77169ec1616af807290548 Mon Sep 17 00:00:00 2001 From: tim neutkens Date: Sat, 28 Jan 2017 22:11:30 +0100 Subject: [PATCH 2/6] Added comment + check if lastScroll was set --- client/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/index.js b/client/index.js index 4fd63e9c94c4a..c10bc14d81c37 100644 --- a/client/index.js +++ b/client/index.js @@ -66,10 +66,12 @@ async function doRender ({ Component, props, err }) { props = await loadGetInitialProps(Component, { err, pathname, query }) } - if (Component === ErrorComponent) { + // Remember scroll when ErrorComponent is being rendered to later restore it + if (!lastScroll && Component === ErrorComponent) { + const { pageXOffset, pageYOffset } = window lastScroll = { - x: window.pageXOffset, - y: window.pageYOffset + x: pageXOffset, + y: pageYOffset } } @@ -83,7 +85,7 @@ async function doRender ({ Component, props, err }) { Component !== ErrorComponent && lastAppProps.Component === ErrorComponent) { // Restore scroll after ErrorComponent was replaced with a page component by HMR - const {x, y} = lastScroll + const { x, y } = lastScroll window.scroll(x, y) lastScroll = false } From e77d78719c0f0cc0b7f2c1f29ce062ccb2d4d462 Mon Sep 17 00:00:00 2001 From: tim neutkens Date: Sun, 29 Jan 2017 11:41:43 +0100 Subject: [PATCH 3/6] Remove check for lastAppProps --- client/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/index.js b/client/index.js index c10bc14d81c37..2430c1110275a 100644 --- a/client/index.js +++ b/client/index.js @@ -81,9 +81,7 @@ async function doRender ({ Component, props, err }) { const appProps = { Component, props, err, router, headManager } ReactDOM.render(createElement(App, appProps), container) - if (lastScroll && - Component !== ErrorComponent && - lastAppProps.Component === ErrorComponent) { + if (lastScroll && Component !== ErrorComponent) { // Restore scroll after ErrorComponent was replaced with a page component by HMR const { x, y } = lastScroll window.scroll(x, y) From 4f8334ea48f19cee6764e291f51f1606a395b9ed Mon Sep 17 00:00:00 2001 From: tim neutkens Date: Sun, 29 Jan 2017 20:00:57 +0100 Subject: [PATCH 4/6] Use events to make scroll persistence dev-only --- client/index.js | 26 +++++++++++--------------- client/next-dev.js | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/client/index.js b/client/index.js index 2430c1110275a..6320462fa621e 100644 --- a/client/index.js +++ b/client/index.js @@ -8,6 +8,7 @@ import evalScript from '../lib/eval-script' import { loadGetInitialProps } from '../lib/utils' const { + CustomEvent, __NEXT_DATA__: { component, errorComponent, @@ -22,7 +23,6 @@ const { const Component = evalScript(component).default const ErrorComponent = evalScript(errorComponent).default let lastAppProps -let lastScroll export const router = createRouter(pathname, query, { Component, @@ -66,14 +66,11 @@ async function doRender ({ Component, props, err }) { props = await loadGetInitialProps(Component, { err, pathname, query }) } - // Remember scroll when ErrorComponent is being rendered to later restore it - if (!lastScroll && Component === ErrorComponent) { - const { pageXOffset, pageYOffset } = window - lastScroll = { - x: pageXOffset, - y: pageYOffset - } - } + // Try/catch is needed because IE11 has a CustomEvent implementation without contructor + try { + const event = new CustomEvent('before-reactdom-render', { detail: { Component } }) + document.dispatchEvent(event) + } catch (e) {} Component = Component || lastAppProps.Component props = props || lastAppProps.props @@ -81,12 +78,11 @@ async function doRender ({ Component, props, err }) { const appProps = { Component, props, err, router, headManager } ReactDOM.render(createElement(App, appProps), container) - if (lastScroll && Component !== ErrorComponent) { - // Restore scroll after ErrorComponent was replaced with a page component by HMR - const { x, y } = lastScroll - window.scroll(x, y) - lastScroll = false - } + // Try/catch is needed because IE11 has a CustomEvent implementation without contructor + try { + const event = new CustomEvent('after-reactdom-render', { detail: { Component } }) + document.dispatchEvent(event) + } catch (e) {} lastAppProps = appProps } diff --git a/client/next-dev.js b/client/next-dev.js index 2ae0ba06f9bb3..c3caf9304d874 100644 --- a/client/next-dev.js +++ b/client/next-dev.js @@ -1,4 +1,8 @@ import patch from './patch-react' +import evalScript from '../lib/eval-script' + +const { __NEXT_DATA__: { errorComponent } } = window +const ErrorComponent = evalScript(errorComponent).default // apply patch first patch((err) => { @@ -20,3 +24,25 @@ function onError (err) { // so that the current component doesn't lose props next.render({ err }) } + +let lastScroll + +document.addEventListener('before-reactdom-render', ({ detail: { Component } }) => { + // Remember scroll when ErrorComponent is being rendered to later restore it + if (!lastScroll && Component === ErrorComponent) { + const { pageXOffset, pageYOffset } = window + lastScroll = { + x: pageXOffset, + y: pageYOffset + } + } +}) + +document.addEventListener('after-reactdom-render', ({ detail: { Component } }) => { + if (lastScroll && Component !== ErrorComponent) { + // Restore scroll after ErrorComponent was replaced with a page component by HMR + const { x, y } = lastScroll + window.scroll(x, y) + lastScroll = false + } +}) From 41525feef63f1d22d65f2e7318af78ecd9d14fdd Mon Sep 17 00:00:00 2001 From: tim neutkens Date: Mon, 30 Jan 2017 23:00:36 +0100 Subject: [PATCH 5/6] Return EventEmitter from next() --- client/index.js | 27 +++++++++++++-------------- client/next-dev.js | 8 ++++---- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/client/index.js b/client/index.js index 6320462fa621e..e167f1269beea 100644 --- a/client/index.js +++ b/client/index.js @@ -1,5 +1,6 @@ import { createElement } from 'react' import ReactDOM from 'react-dom' +import { EventEmitter } from 'events' import HeadManager from './head-manager' import { rehydrate } from '../lib/css' import { createRouter } from '../lib/router' @@ -8,7 +9,6 @@ import evalScript from '../lib/eval-script' import { loadGetInitialProps } from '../lib/utils' const { - CustomEvent, __NEXT_DATA__: { component, errorComponent, @@ -34,13 +34,16 @@ const headManager = new HeadManager() const container = document.getElementById('__next') export default (onError) => { + const emitter = new EventEmitter() if (ids && ids.length) rehydrate(ids) router.subscribe(({ Component, props, err }) => { - render({ Component, props, err }, onError) + render({ Component, props, err, emitter }, onError) }) - render({ Component, props, err }, onError) + render({ Component, props, err, emitter }, onError) + + return emitter } export async function render (props, onError = renderErrorComponent) { @@ -57,7 +60,7 @@ async function renderErrorComponent (err) { await doRender({ Component: ErrorComponent, props, err }) } -async function doRender ({ Component, props, err }) { +async function doRender ({ Component, props, err, emitter }) { if (!props && Component && Component !== ErrorComponent && lastAppProps.Component === ErrorComponent) { @@ -66,11 +69,9 @@ async function doRender ({ Component, props, err }) { props = await loadGetInitialProps(Component, { err, pathname, query }) } - // Try/catch is needed because IE11 has a CustomEvent implementation without contructor - try { - const event = new CustomEvent('before-reactdom-render', { detail: { Component } }) - document.dispatchEvent(event) - } catch (e) {} + if (emitter) { + emitter.emit('before-reactdom-render', { Component }) + } Component = Component || lastAppProps.Component props = props || lastAppProps.props @@ -78,11 +79,9 @@ async function doRender ({ Component, props, err }) { const appProps = { Component, props, err, router, headManager } ReactDOM.render(createElement(App, appProps), container) - // Try/catch is needed because IE11 has a CustomEvent implementation without contructor - try { - const event = new CustomEvent('after-reactdom-render', { detail: { Component } }) - document.dispatchEvent(event) - } catch (e) {} + if (emitter) { + emitter.emit('after-reactdom-render', { Component }) + } lastAppProps = appProps } diff --git a/client/next-dev.js b/client/next-dev.js index c3caf9304d874..1f3a8f5100b80 100644 --- a/client/next-dev.js +++ b/client/next-dev.js @@ -17,17 +17,17 @@ require('react-hot-loader/patch') const next = window.next = require('./') -next.default(onError) +const emitter = next.default(onError) function onError (err) { // just show the debug screen but don't render ErrorComponent // so that the current component doesn't lose props - next.render({ err }) + next.render({ err, emitter }) } let lastScroll -document.addEventListener('before-reactdom-render', ({ detail: { Component } }) => { +emitter.on('before-reactdom-render', ({ Component }) => { // Remember scroll when ErrorComponent is being rendered to later restore it if (!lastScroll && Component === ErrorComponent) { const { pageXOffset, pageYOffset } = window @@ -38,7 +38,7 @@ document.addEventListener('before-reactdom-render', ({ detail: { Component } }) } }) -document.addEventListener('after-reactdom-render', ({ detail: { Component } }) => { +emitter.on('after-reactdom-render', ({ Component }) => { if (lastScroll && Component !== ErrorComponent) { // Restore scroll after ErrorComponent was replaced with a page component by HMR const { x, y } = lastScroll From d3ba6a22395c0ad55ba78bd72a028497606c7a10 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Tue, 31 Jan 2017 08:49:50 +0530 Subject: [PATCH 6/6] Update next-dev.js --- client/next-dev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/next-dev.js b/client/next-dev.js index 1f3a8f5100b80..00c1dd93ce751 100644 --- a/client/next-dev.js +++ b/client/next-dev.js @@ -43,6 +43,6 @@ emitter.on('after-reactdom-render', ({ Component }) => { // Restore scroll after ErrorComponent was replaced with a page component by HMR const { x, y } = lastScroll window.scroll(x, y) - lastScroll = false + lastScroll = null } })