@@ -34,12 +34,12 @@ const stack = captureOwnerStack();
34
34
35
35
Call ` captureOwnerStack ` to get the current Owner Stack.
36
36
37
- ``` js
38
- import * as React from ' react' ;
37
+ ``` js {5,5}
38
+ import { captureOwnerStack } from ' react' ;
39
39
40
40
function Component () {
41
41
if (process .env .NODE_ENV !== ' production' ) {
42
- const ownerStack = React . captureOwnerStack ();
42
+ const ownerStack = captureOwnerStack ();
43
43
console .log (ownerStack);
44
44
}
45
45
}
@@ -68,17 +68,18 @@ The Owner Stack is different from the Component Stack available in React error h
68
68
69
69
For example, consider the following code:
70
70
71
- ``` tsx
72
- import * as React from ' react' ;
73
- import * as ReactDOMClient from ' react-dom/client' ;
71
+ <Sandpack >
72
+
73
+ ``` js src/App.js
74
+ import {Suspense } from ' react' ;
74
75
75
76
function SubComponent ({disabled}) {
76
77
if (disabled) {
77
78
throw new Error (' disabled' );
78
79
}
79
80
}
80
81
81
- function Component({label }) {
82
+ export function Component ({label}) {
82
83
return (
83
84
< fieldset>
84
85
< legend> {label}< / legend>
@@ -91,24 +92,70 @@ function Navigation() {
91
92
return null ;
92
93
}
93
94
94
- function App({children }) {
95
+ export default function App ({children}) {
95
96
return (
96
- <React. Suspense fallback = " loading..." >
97
+ < Suspense fallback= " loading..." >
97
98
< main>
98
99
< Navigation / >
99
100
{children}
100
101
< / main>
101
- </React. Suspense >
102
+ < / Suspense>
102
103
);
103
104
}
105
+ ```
104
106
105
- createRoot (document .createElement (' div' )).render (
107
+ ``` js src/index.js
108
+ import {captureOwnerStack } from ' react' ;
109
+ import {createRoot } from ' react-dom/client' ;
110
+ import App , {Component } from ' ./App.js' ;
111
+ import ' ./styles.css' ;
112
+
113
+ createRoot (document .createElement (' div' ), {
114
+ onUncaughtError : (error , errorInfo ) => {
115
+ // The stacks are logged instead of showing them in the UI directly to highlight that browsers will apply sourcemaps to the logged stacks.
116
+ // Note that sourcemapping is only applied in the real browser console not in the fake one displayed on this page.
117
+ console .log (errorInfo .componentStack );
118
+ console .log (captureOwnerStack ());
119
+ },
120
+ }).render (
106
121
< App>
107
122
< Component label= " disabled" / >
108
123
< / App>
109
124
);
110
125
```
111
126
127
+ ``` json package.json hidden
128
+ {
129
+ "dependencies" : {
130
+ "react" : " experimental" ,
131
+ "react-dom" : " experimental" ,
132
+ "react-scripts" : " latest"
133
+ },
134
+ "scripts" : {
135
+ "start" : " react-scripts start" ,
136
+ "build" : " react-scripts build" ,
137
+ "test" : " react-scripts test --env=jsdom" ,
138
+ "eject" : " react-scripts eject"
139
+ }
140
+ }
141
+ ```
142
+
143
+ ``` html public/index.html hidden
144
+ <!DOCTYPE html>
145
+ <html lang =" en" >
146
+ <head >
147
+ <meta charset =" UTF-8" />
148
+ <meta name =" viewport" content =" width=device-width, initial-scale=1.0" />
149
+ <title >Document</title >
150
+ </head >
151
+ <body >
152
+ <p >Check the console output.</p >
153
+ </body >
154
+ </html >
155
+ ```
156
+
157
+ </Sandpack >
158
+
112
159
` SubComponent ` would throw an error.
113
160
The Component Stack of that error would be
114
161
@@ -124,14 +171,15 @@ at App
124
171
However, the Owner Stack would only read
125
172
126
173
```
127
- at SubComponent
128
174
at Component
129
175
```
130
176
131
177
Neither ` App ` nor the DOM components (e.g. ` fieldset ` ) are considered Owners in this Stack since they didn't contribute to "creating" the node containing ` SubComponent ` . ` App ` and DOM components only forwarded the node. ` App ` just rendered the ` children ` node as opposed to ` Component ` which created a node containing ` SubComponent ` via ` <SubComponent /> ` .
132
178
133
179
Neither ` Navigation ` nor ` legend ` are in the stack at all since it's only a sibling to a node containing ` <SubComponent /> ` .
134
180
181
+ ` SubComponent ` is omitted because it's already part of the callstack.
182
+
135
183
</DeepDive >
136
184
137
185
## Usage {/* usage* /}
@@ -141,30 +189,79 @@ Neither `Navigation` nor `legend` are in the stack at all since it's only a sibl
141
189
In addition to the stack trace of the <CodeStep step ={1} >error</CodeStep > itself, you can use <CodeStep step ={2} >` captureOwnerStack ` </CodeStep > to append the Owner Stack.
142
190
This can help trace the error especially when the error is caused by props. The Owner Stack helps trace the flow of props.
143
191
144
- ``` jsx [[9, 15, "error"], [34, 10, "captureOwnerStack"]]
145
- import { captureOwnerStack } from ' react'
146
- import { hydrateRoot } from ' react-dom/client' ;
147
-
148
- const root = hydrateRoot (
149
- document .getElementById (' root' ),
150
- < App / > ,
151
- {
152
- onCaughtError : (error , errorInfo ) => {
153
- if (process .env .NODE_ENV !== ' production' ) {
154
- const ownerStack = captureOwnerStack ();
155
- error .stack += ownerStack;
156
- }
157
- console .error (
158
- ' Caught error' ,
159
- error,
160
- errorInfo .componentStack
161
- );
192
+ <Sandpack >
193
+
194
+ ``` js
195
+ function useCustomHook () {
196
+ throw new Error (' Boom!' );
197
+ }
198
+
199
+ function Component () {
200
+ useCustomHook ();
201
+ }
202
+
203
+ export default function App () {
204
+ return < Component / > ;
205
+ }
206
+ ```
207
+
208
+ ``` js src/index.js [[9, 15, "error"], [34, 10, "captureOwnerStack"]]
209
+ import {captureOwnerStack } from ' react' ;
210
+ import {createRoot } from ' react-dom/client' ;
211
+ import App from ' ./App.js' ;
212
+ import ' ./styles.css' ;
213
+
214
+ const root = createRoot (document .getElementById (' root' ), {
215
+ onUncaughtError : (error , errorInfo ) => {
216
+ if (process .env .NODE_ENV !== ' production' ) {
217
+ const ownerStack = captureOwnerStack ();
218
+ error .stack =
219
+ // The stack is only split because these sandboxes don't implement ignore-listing 3rd party frames via sourcemaps.
220
+ // A framework would ignore-list stackframes from React via sourcemaps and then you could just `error.stack += ownerStack`.
221
+ // To learn more about ignore-listing see https://developer.chrome.com/docs/devtools/x-google-ignore-list
222
+ error .stack .split (' \n at react-stack-bottom-frame' )[0 ] + ownerStack;
162
223
}
224
+
225
+ // The stacks are logged instead of showing them in the UI directly to highlight that browsers will apply sourcemaps to the logged stacks.
226
+ // Note that sourcemapping is only applied in the real browser console not in the fake one displayed on this page.
227
+ console .error (' Uncaught' , error);
228
+ },
229
+ }).render (< App / > );
230
+ ```
231
+
232
+ ``` json package.json hidden
233
+ {
234
+ "dependencies" : {
235
+ "react" : " experimental" ,
236
+ "react-dom" : " experimental" ,
237
+ "react-scripts" : " latest"
238
+ },
239
+ "scripts" : {
240
+ "start" : " react-scripts start" ,
241
+ "build" : " react-scripts build" ,
242
+ "test" : " react-scripts test --env=jsdom" ,
243
+ "eject" : " react-scripts eject"
163
244
}
164
- );
165
- root .render (< App / > );
245
+ }
166
246
```
167
247
248
+ ``` html public/index.html hidden
249
+ <!DOCTYPE html>
250
+ <html lang =" en" >
251
+ <head >
252
+ <meta charset =" UTF-8" />
253
+ <meta name =" viewport" content =" width=device-width, initial-scale=1.0" />
254
+ <title >Document</title >
255
+ </head >
256
+ <body >
257
+ <p >Check the console output.</p >
258
+ <div id =" root" ></div >
259
+ </body >
260
+ </html >
261
+ ```
262
+
263
+ </Sandpack >
264
+
168
265
## Troubleshooting {/* troubleshooting* /}
169
266
170
267
### The Owner Stack is ` null ` {/* the-owner-stack-is-null* /}
0 commit comments