@@ -6,6 +6,7 @@ import { createRouter } from '../lib/router'
66import App from '../lib/app'
77import evalScript from '../lib/eval-script'
88import { loadGetInitialProps , getURL } from '../lib/utils'
9+ import ErrorDebugComponent from '../lib/error-debug'
910
1011// Polyfill Promise globally
1112// This is needed because Webpack2's dynamic loading(common chunks) code
@@ -39,33 +40,57 @@ export const router = createRouter(pathname, query, getURL(), {
3940} )
4041
4142const headManager = new HeadManager ( )
42- const container = document . getElementById ( '__next' )
43+ const appContainer = document . getElementById ( '__next' )
44+ const errorContainer = document . getElementById ( '__next-error' )
4345
44- export default ( onError ) => {
46+ export default ( ) => {
4547 const emitter = mitt ( )
4648
4749 router . subscribe ( ( { Component, props, hash, err } ) => {
48- render ( { Component, props, err, hash, emitter } , onError )
50+ render ( { Component, props, err, hash, emitter } )
4951 } )
5052
5153 const hash = location . hash . substring ( 1 )
52- render ( { Component, props, hash, err, emitter } , onError )
54+ render ( { Component, props, hash, err, emitter } )
5355
5456 return emitter
5557}
5658
57- export async function render ( props , onError = renderErrorComponent ) {
59+ export async function render ( props ) {
60+ if ( props . err ) {
61+ await renderError ( props . err )
62+ return
63+ }
64+
5865 try {
5966 await doRender ( props )
6067 } catch ( err ) {
61- await onError ( err )
68+ if ( err . abort ) return
69+ await renderError ( err )
6270 }
6371}
6472
65- async function renderErrorComponent ( err ) {
66- const { pathname, query } = router
67- const props = await loadGetInitialProps ( ErrorComponent , { err, pathname, query } )
68- await doRender ( { Component : ErrorComponent , props, err } )
73+ // This method handles all runtime and debug errors.
74+ // 404 and 500 errors are special kind of errors
75+ // and they are still handle via the main render method.
76+ export async function renderError ( error ) {
77+ const prod = process . env . NODE_ENV === 'production'
78+ // We need to unmount the current app component because it's
79+ // in the inconsistant state.
80+ // Otherwise, we need to face issues when the issue is fixed and
81+ // it's get notified via HMR
82+ ReactDOM . unmountComponentAtNode ( appContainer )
83+
84+ const errorMessage = `${ error . message } \n${ error . stack } `
85+ console . error ( errorMessage )
86+
87+ if ( prod ) {
88+ const initProps = { err : error , pathname, query }
89+ const props = await loadGetInitialProps ( ErrorComponent , initProps )
90+ ReactDOM . render ( createElement ( ErrorComponent , props ) , errorContainer )
91+ } else {
92+ ReactDOM . render ( createElement ( ErrorDebugComponent , { error } ) , errorContainer )
93+ }
6994}
7095
7196async function doRender ( { Component, props, hash, err, emitter } ) {
@@ -88,7 +113,9 @@ async function doRender ({ Component, props, hash, err, emitter }) {
88113 // lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error.
89114 lastAppProps = appProps
90115
91- ReactDOM . render ( createElement ( App , appProps ) , container )
116+ // We need to clear any existing runtime error messages
117+ ReactDOM . unmountComponentAtNode ( errorContainer )
118+ ReactDOM . render ( createElement ( App , appProps ) , appContainer )
92119
93120 if ( emitter ) {
94121 emitter . emit ( 'after-reactdom-render' , { Component } )
0 commit comments