mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Handle errors of React lifecycle methods (#661)
* handle errors of lifecycle methods * handle errors of render method
This commit is contained in:
parent
e01056d3f1
commit
8570d19af6
|
@ -1,4 +1,11 @@
|
||||||
import 'react-hot-loader/patch'
|
import 'react-hot-loader/patch'
|
||||||
import * as next from './next'
|
import patch from './patch-react'
|
||||||
|
|
||||||
|
// apply patch first
|
||||||
|
patch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
next.renderError(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
const next = require('./next')
|
||||||
window.next = next
|
window.next = next
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createElement } from 'react'
|
import { createElement } from 'react'
|
||||||
import { render } from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import HeadManager from './head-manager'
|
import HeadManager from './head-manager'
|
||||||
import { rehydrate } from '../lib/css'
|
import { rehydrate } from '../lib/css'
|
||||||
import { createRouter } from '../lib/router'
|
import { createRouter } from '../lib/router'
|
||||||
|
@ -29,7 +29,31 @@ export const router = createRouter(pathname, query, {
|
||||||
|
|
||||||
const headManager = new HeadManager()
|
const headManager = new HeadManager()
|
||||||
const container = document.getElementById('__next')
|
const container = document.getElementById('__next')
|
||||||
const appProps = { Component, props, router, headManager }
|
const defaultProps = { Component, ErrorComponent, props, router, headManager }
|
||||||
|
|
||||||
if (ids && ids.length) rehydrate(ids)
|
if (ids && ids.length) rehydrate(ids)
|
||||||
render(createElement(App, appProps), container)
|
|
||||||
|
render()
|
||||||
|
|
||||||
|
export function render (props = {}) {
|
||||||
|
try {
|
||||||
|
doRender(props)
|
||||||
|
} catch (err) {
|
||||||
|
renderError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renderError (err) {
|
||||||
|
const { pathname, query } = router
|
||||||
|
const props = await ErrorComponent.getInitialProps({ err, pathname, query })
|
||||||
|
try {
|
||||||
|
doRender({ Component: ErrorComponent, props })
|
||||||
|
} catch (err2) {
|
||||||
|
console.error(err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doRender (props) {
|
||||||
|
const appProps = { ...defaultProps, ...props }
|
||||||
|
ReactDOM.render(createElement(App, appProps), container)
|
||||||
|
}
|
||||||
|
|
62
client/patch-react.js
Normal file
62
client/patch-react.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
// monkeypatch React for fixing https://github.com/facebook/react/issues/2461
|
||||||
|
// based on https://gist.github.com/Aldredcz/4d63b0a9049b00f54439f8780be7f0d8
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
let patched = false
|
||||||
|
|
||||||
|
export default (handleError = () => {}) => {
|
||||||
|
if (patched) {
|
||||||
|
throw new Error('React is already monkeypatched')
|
||||||
|
}
|
||||||
|
|
||||||
|
patched = true
|
||||||
|
|
||||||
|
const { createElement } = React
|
||||||
|
|
||||||
|
React.createElement = function (Component, ...rest) {
|
||||||
|
if (typeof Component === 'function') {
|
||||||
|
const { prototype } = Component
|
||||||
|
if (prototype && prototype.render) {
|
||||||
|
prototype.render = wrapRender(prototype.render)
|
||||||
|
} else {
|
||||||
|
// stateless component
|
||||||
|
Component = wrapRender(Component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createElement.call(this, Component, ...rest)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Component: { prototype: componentPrototype } } = React
|
||||||
|
const { forceUpdate } = componentPrototype
|
||||||
|
|
||||||
|
componentPrototype.forceUpdate = function (...args) {
|
||||||
|
if (this.render) {
|
||||||
|
this.render = wrapRender(this.render)
|
||||||
|
}
|
||||||
|
return forceUpdate.apply(this, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapRender (render) {
|
||||||
|
if (render.__wrapped) {
|
||||||
|
return render.__wrapped
|
||||||
|
}
|
||||||
|
|
||||||
|
const _render = function (...args) {
|
||||||
|
try {
|
||||||
|
return render.apply(this, args)
|
||||||
|
} catch (err) {
|
||||||
|
handleError(err)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy all properties
|
||||||
|
Object.assign(_render, render)
|
||||||
|
|
||||||
|
render.__wrapped = _render.__wrapped = _render
|
||||||
|
|
||||||
|
return _render
|
||||||
|
}
|
||||||
|
}
|
19
lib/app.js
19
lib/app.js
|
@ -19,7 +19,7 @@ export default class App extends Component {
|
||||||
try {
|
try {
|
||||||
this.setState(state)
|
this.setState(state)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
this.handleError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export default class App extends Component {
|
||||||
try {
|
try {
|
||||||
this.setState(state)
|
this.setState(state)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
this.handleError(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,21 @@ export default class App extends Component {
|
||||||
<Component {...props} />
|
<Component {...props} />
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleError (err) {
|
||||||
|
console.error(err)
|
||||||
|
|
||||||
|
const { router, ErrorComponent } = this.props
|
||||||
|
const { pathname, query } = router
|
||||||
|
const props = await ErrorComponent.getInitialProps({ err, pathname, query })
|
||||||
|
const state = propsToState({ Component: ErrorComponent, props, router })
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.setState(state)
|
||||||
|
} catch (err2) {
|
||||||
|
console.error(err2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function propsToState (props) {
|
function propsToState (props) {
|
||||||
|
|
Loading…
Reference in a new issue