Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Remember scroll position on error #911

Merged
merged 6 commits into from
Jan 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions client/index.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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
}
30 changes: 28 additions & 2 deletions client/next-dev.js
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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
}
})