@@ -6,6 +6,7 @@ import { createRouter } from '../lib/router'
6
6
import App from '../lib/app'
7
7
import evalScript from '../lib/eval-script'
8
8
import { loadGetInitialProps , getURL } from '../lib/utils'
9
+ import ErrorDebugComponent from '../lib/error-debug'
9
10
10
11
// Polyfill Promise globally
11
12
// This is needed because Webpack2's dynamic loading(common chunks) code
@@ -39,33 +40,57 @@ export const router = createRouter(pathname, query, getURL(), {
39
40
} )
40
41
41
42
const headManager = new HeadManager ( )
42
- const container = document . getElementById ( '__next' )
43
+ const appContainer = document . getElementById ( '__next' )
44
+ const errorContainer = document . getElementById ( '__next-error' )
43
45
44
- export default ( onError ) => {
46
+ export default ( ) => {
45
47
const emitter = mitt ( )
46
48
47
49
router . subscribe ( ( { Component, props, hash, err } ) => {
48
- render ( { Component, props, err, hash, emitter } , onError )
50
+ render ( { Component, props, err, hash, emitter } )
49
51
} )
50
52
51
53
const hash = location . hash . substring ( 1 )
52
- render ( { Component, props, hash, err, emitter } , onError )
54
+ render ( { Component, props, hash, err, emitter } )
53
55
54
56
return emitter
55
57
}
56
58
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
+
58
65
try {
59
66
await doRender ( props )
60
67
} catch ( err ) {
61
- await onError ( err )
68
+ if ( err . abort ) return
69
+ await renderError ( err )
62
70
}
63
71
}
64
72
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
+ }
69
94
}
70
95
71
96
async function doRender ( { Component, props, hash, err, emitter } ) {
@@ -88,7 +113,9 @@ async function doRender ({ Component, props, hash, err, emitter }) {
88
113
// lastAppProps has to be set before ReactDom.render to account for ReactDom throwing an error.
89
114
lastAppProps = appProps
90
115
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 )
92
119
93
120
if ( emitter ) {
94
121
emitter . emit ( 'after-reactdom-render' , { Component } )
0 commit comments