@@ -34,12 +34,12 @@ const stack = captureOwnerStack();
3434
3535Call ` captureOwnerStack ` to get the current Owner Stack.
3636
37- ``` js
38- import * as React from ' react' ;
37+ ``` js {5,5}
38+ import { captureOwnerStack } from ' react' ;
3939
4040function Component () {
4141 if (process .env .NODE_ENV !== ' production' ) {
42- const ownerStack = React . captureOwnerStack ();
42+ const ownerStack = captureOwnerStack ();
4343 console .log (ownerStack);
4444 }
4545}
@@ -68,17 +68,18 @@ The Owner Stack is different from the Component Stack available in React error h
6868
6969For example, consider the following code:
7070
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' ;
7475
7576function SubComponent ({disabled}) {
7677 if (disabled) {
7778 throw new Error (' disabled' );
7879 }
7980}
8081
81- function Component({label }) {
82+ export function Component ({label}) {
8283 return (
8384 < fieldset>
8485 < legend> {label}< / legend>
@@ -91,24 +92,70 @@ function Navigation() {
9192 return null ;
9293}
9394
94- function App({children }) {
95+ export default function App ({children}) {
9596 return (
96- <React. Suspense fallback = " loading..." >
97+ < Suspense fallback= " loading..." >
9798 < main>
9899 < Navigation / >
99100 {children}
100101 < / main>
101- </React. Suspense >
102+ < / Suspense>
102103 );
103104}
105+ ```
104106
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 (
106121 < App>
107122 < Component label= " disabled" / >
108123 < / App>
109124);
110125```
111126
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+
112159` SubComponent ` would throw an error.
113160The Component Stack of that error would be
114161
@@ -124,47 +171,114 @@ at App
124171However, the Owner Stack would only read
125172
126173```
127- at SubComponent
128174at Component
129175```
130176
131177Neither ` 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 /> ` .
132178
133179Neither ` Navigation ` nor ` legend ` are in the stack at all since it's only a sibling to a node containing ` <SubComponent /> ` .
134180
181+ ` SubComponent ` is omitted because it's already part of the callstack.
182+
135183</DeepDive >
136184
137185## Usage {/* usage* /}
138186
139187### Expanding error stacks {/* expanding-error-stacks* /}
140188
141- 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.
189+ In addition to the <CodeStep step ={1} >stack trace of the error</CodeStep > itself, you can use <CodeStep step ={2} >` captureOwnerStack ` </CodeStep > to append the Owner Stack.
142190This can help trace the error especially when the error is caused by props. The Owner Stack helps trace the flow of props.
143191
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+
193+ ``` js src/index.js [[1, 8, "error.stack"], [2, 7, "captureOwnerStack()"]]
194+ import {captureOwnerStack } from ' react' ;
195+ import {createRoot } from ' react-dom/client' ;
196+
197+ const root = createRoot (document .getElementById (' root' ), {
198+ onUncaughtError : (error , errorInfo ) => {
199+ if (process .env .NODE_ENV !== ' production' ) {
200+ const ownerStack = captureOwnerStack ();
201+ error .stack += ownerStack;
202+ }
203+
204+ console .error (' Uncaught' , error);
205+ },
206+ }).render (< App / > );
207+ ```
208+
209+ <Sandpack >
210+
211+ ``` js
212+ function useCustomHook () {
213+ throw new Error (' Boom!' );
214+ }
215+
216+ function Component () {
217+ useCustomHook ();
218+ }
219+
220+ export default function App () {
221+ return < Component / > ;
222+ }
223+ ```
224+
225+ ``` js src/index.js
226+ import {captureOwnerStack } from ' react' ;
227+ import {createRoot } from ' react-dom/client' ;
228+ import App from ' ./App.js' ;
229+ import ' ./styles.css' ;
230+
231+ const root = createRoot (document .getElementById (' root' ), {
232+ onUncaughtError : (error , errorInfo ) => {
233+ if (process .env .NODE_ENV !== ' production' ) {
234+ const ownerStack = captureOwnerStack ();
235+ error .stack =
236+ // The stack is only split because these sandboxes don't implement ignore-listing 3rd party frames via sourcemaps.
237+ // A framework would ignore-list stackframes from React via sourcemaps and then you could just `error.stack += ownerStack`.
238+ // To learn more about ignore-listing see https://developer.chrome.com/docs/devtools/x-google-ignore-list
239+ error .stack .split (' \n at react-stack-bottom-frame' )[0 ] + ownerStack;
162240 }
241+
242+ // The stacks are logged instead of showing them in the UI directly to highlight that browsers will apply sourcemaps to the logged stacks.
243+ // Note that sourcemapping is only applied in the real browser console not in the fake one displayed on this page.
244+ console .error (' Uncaught' , error);
245+ },
246+ }).render (< App / > );
247+ ```
248+
249+ ``` json package.json hidden
250+ {
251+ "dependencies" : {
252+ "react" : " experimental" ,
253+ "react-dom" : " experimental" ,
254+ "react-scripts" : " latest"
255+ },
256+ "scripts" : {
257+ "start" : " react-scripts start" ,
258+ "build" : " react-scripts build" ,
259+ "test" : " react-scripts test --env=jsdom" ,
260+ "eject" : " react-scripts eject"
163261 }
164- );
165- root .render (< App / > );
262+ }
263+ ```
264+
265+ ``` html public/index.html hidden
266+ <!DOCTYPE html>
267+ <html lang =" en" >
268+ <head >
269+ <meta charset =" UTF-8" />
270+ <meta name =" viewport" content =" width=device-width, initial-scale=1.0" />
271+ <title >Document</title >
272+ </head >
273+ <body >
274+ <p >Check the console output.</p >
275+ <div id =" root" ></div >
276+ </body >
277+ </html >
166278```
167279
280+ </Sandpack >
281+
168282## Troubleshooting {/* troubleshooting* /}
169283
170284### The Owner Stack is ` null ` {/* the-owner-stack-is-null* /}
0 commit comments