Skip to content

Commit bd83ed5

Browse files
timneutkensarunoda
authored andcommitted
[WIP] Remember scroll position on error (#911)
* Remember scroll position on error * Added comment + check if lastScroll was set * Remove check for lastAppProps * Use events to make scroll persistence dev-only * Return EventEmitter from next() * Update next-dev.js
1 parent 08eadb4 commit bd83ed5

File tree

2 files changed

+45
-6
lines changed

2 files changed

+45
-6
lines changed

client/index.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createElement } from 'react'
22
import ReactDOM from 'react-dom'
3+
import { EventEmitter } from 'events'
34
import HeadManager from './head-manager'
45
import { rehydrate } from '../lib/css'
56
import { createRouter } from '../lib/router'
@@ -33,13 +34,16 @@ const headManager = new HeadManager()
3334
const container = document.getElementById('__next')
3435

3536
export default (onError) => {
37+
const emitter = new EventEmitter()
3638
if (ids && ids.length) rehydrate(ids)
3739

3840
router.subscribe(({ Component, props, err }) => {
39-
render({ Component, props, err }, onError)
41+
render({ Component, props, err, emitter }, onError)
4042
})
4143

42-
render({ Component, props, err }, onError)
44+
render({ Component, props, err, emitter }, onError)
45+
46+
return emitter
4347
}
4448

4549
export async function render (props, onError = renderErrorComponent) {
@@ -56,7 +60,7 @@ async function renderErrorComponent (err) {
5660
await doRender({ Component: ErrorComponent, props, err })
5761
}
5862

59-
async function doRender ({ Component, props, err }) {
63+
async function doRender ({ Component, props, err, emitter }) {
6064
if (!props && Component &&
6165
Component !== ErrorComponent &&
6266
lastAppProps.Component === ErrorComponent) {
@@ -65,10 +69,19 @@ async function doRender ({ Component, props, err }) {
6569
props = await loadGetInitialProps(Component, { err, pathname, query })
6670
}
6771

72+
if (emitter) {
73+
emitter.emit('before-reactdom-render', { Component })
74+
}
75+
6876
Component = Component || lastAppProps.Component
6977
props = props || lastAppProps.props
7078

7179
const appProps = { Component, props, err, router, headManager }
72-
lastAppProps = appProps
7380
ReactDOM.render(createElement(App, appProps), container)
81+
82+
if (emitter) {
83+
emitter.emit('after-reactdom-render', { Component })
84+
}
85+
86+
lastAppProps = appProps
7487
}

client/next-dev.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import patch from './patch-react'
2+
import evalScript from '../lib/eval-script'
3+
4+
const { __NEXT_DATA__: { errorComponent } } = window
5+
const ErrorComponent = evalScript(errorComponent).default
26

37
// apply patch first
48
patch((err) => {
@@ -13,10 +17,32 @@ require('react-hot-loader/patch')
1317

1418
const next = window.next = require('./')
1519

16-
next.default(onError)
20+
const emitter = next.default(onError)
1721

1822
function onError (err) {
1923
// just show the debug screen but don't render ErrorComponent
2024
// so that the current component doesn't lose props
21-
next.render({ err })
25+
next.render({ err, emitter })
2226
}
27+
28+
let lastScroll
29+
30+
emitter.on('before-reactdom-render', ({ Component }) => {
31+
// Remember scroll when ErrorComponent is being rendered to later restore it
32+
if (!lastScroll && Component === ErrorComponent) {
33+
const { pageXOffset, pageYOffset } = window
34+
lastScroll = {
35+
x: pageXOffset,
36+
y: pageYOffset
37+
}
38+
}
39+
})
40+
41+
emitter.on('after-reactdom-render', ({ Component }) => {
42+
if (lastScroll && Component !== ErrorComponent) {
43+
// Restore scroll after ErrorComponent was replaced with a page component by HMR
44+
const { x, y } = lastScroll
45+
window.scroll(x, y)
46+
lastScroll = null
47+
}
48+
})

0 commit comments

Comments
 (0)