diff --git a/client/index.js b/client/index.js index 4d5c847a..dac29da8 100644 --- a/client/index.js +++ b/client/index.js @@ -6,7 +6,6 @@ import { createRouter } from '../lib/router' import App from '../lib/app' import evalScript from '../lib/eval-script' import { loadGetInitialProps, getURL } from '../lib/utils' -import ErrorDebugComponent from '../lib/error-debug' // Polyfill Promise globally // This is needed because Webpack2's dynamic loading(common chunks) code @@ -40,57 +39,33 @@ export const router = createRouter(pathname, query, getURL(), { }) const headManager = new HeadManager() -const appContainer = document.getElementById('__next') -const errorContainer = document.getElementById('__next-error') +const container = document.getElementById('__next') -export default () => { +export default (onError) => { const emitter = mitt() router.subscribe(({ Component, props, hash, err }) => { - render({ Component, props, err, hash, emitter }) + render({ Component, props, err, hash, emitter }, onError) }) const hash = location.hash.substring(1) - render({ Component, props, hash, err, emitter }) + render({ Component, props, hash, err, emitter }, onError) return emitter } -export async function render (props) { - if (props.err) { - await renderError(props.err) - return - } - +export async function render (props, onError = renderErrorComponent) { try { await doRender(props) } catch (err) { - if (err.abort) return - await renderError(err) + await onError(err) } } -// This method handles all runtime and debug errors. -// 404 and 500 errors are special kind of errors -// and they are still handle via the main render method. -export async function renderError (error) { - const prod = process.env.NODE_ENV === 'production' - // We need to unmount the current app component because it's - // in the inconsistant state. - // Otherwise, we need to face issues when the issue is fixed and - // it's get notified via HMR - ReactDOM.unmountComponentAtNode(appContainer) - - const errorMessage = `${error.message}\n${error.stack}` - console.error(errorMessage) - - if (prod) { - const initProps = { err: error, pathname, query } - const props = await loadGetInitialProps(ErrorComponent, initProps) - ReactDOM.render(createElement(ErrorComponent, props), errorContainer) - } else { - ReactDOM.render(createElement(ErrorDebugComponent, { error }), errorContainer) - } +async function renderErrorComponent (err) { + const { pathname, query } = router + const props = await loadGetInitialProps(ErrorComponent, { err, pathname, query }) + await doRender({ Component: ErrorComponent, props, err }) } async function doRender ({ Component, props, hash, err, emitter }) { @@ -113,9 +88,7 @@ async function doRender ({ Component, props, hash, err, emitter }) { // lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error. lastAppProps = appProps - // We need to clear any existing runtime error messages - ReactDOM.unmountComponentAtNode(errorContainer) - ReactDOM.render(createElement(App, appProps), appContainer) + ReactDOM.render(createElement(App, appProps), container) if (emitter) { emitter.emit('after-reactdom-render', { Component }) diff --git a/client/next-dev.js b/client/next-dev.js index 071ec03d..ca113212 100644 --- a/client/next-dev.js +++ b/client/next-dev.js @@ -1,5 +1,4 @@ import evalScript from '../lib/eval-script' -import ReactReconciler from 'react-dom/lib/ReactReconciler' const { __NEXT_DATA__: { errorComponent } } = window const ErrorComponent = evalScript(errorComponent).default @@ -8,18 +7,12 @@ require('react-hot-loader/patch') const next = window.next = require('./') -const emitter = next.default() +const emitter = next.default(onError) -// This is a patch to catch most of the errors throw inside React components. -const originalMountComponent = ReactReconciler.mountComponent -ReactReconciler.mountComponent = function (...args) { - try { - return originalMountComponent(...args) - } catch (err) { - next.renderError(err) - err.abort = true - throw err - } +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, emitter }) } let lastScroll diff --git a/lib/app.js b/lib/app.js index 2cc4d575..603d3b89 100644 --- a/lib/app.js +++ b/lib/app.js @@ -17,7 +17,7 @@ export default class App extends Component { } render () { - const { Component, props, hash, router } = this.props + const { Component, props, hash, err, router } = this.props const url = createUrl(router) // If there no component exported we can't proceed. // We'll tackle that here. @@ -28,6 +28,7 @@ export default class App extends Component { return