diff --git a/client/index.js b/client/index.js index 626fee5d0a806..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' @@ -33,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) { @@ -56,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) { @@ -65,10 +69,19 @@ async function doRender ({ Component, props, err }) { props = await loadGetInitialProps(Component, { err, pathname, query }) } + if (emitter) { + emitter.emit('before-reactdom-render', { Component }) + } + Component = Component || lastAppProps.Component props = props || lastAppProps.props const appProps = { Component, props, err, router, headManager } - lastAppProps = appProps ReactDOM.render(createElement(App, appProps), container) + + if (emitter) { + emitter.emit('after-reactdom-render', { Component }) + } + + lastAppProps = appProps } diff --git a/client/next-dev.js b/client/next-dev.js index 2ae0ba06f9bb3..00c1dd93ce751 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) => { @@ -13,10 +17,32 @@ 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 + +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 + lastScroll = { + x: pageXOffset, + y: pageYOffset + } + } +}) + +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 + window.scroll(x, y) + lastScroll = null + } +})