Skip to content

Commit e88cfc8

Browse files
committed
Move code into sandboxes
1 parent 267c0f5 commit e88cfc8

File tree

1 file changed

+129
-32
lines changed

1 file changed

+129
-32
lines changed

src/content/reference/react/captureOwnerStack.md

Lines changed: 129 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ const stack = captureOwnerStack();
3434

3535
Call `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

4040
function 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

6969
For 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

7576
function 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.
113160
The Component Stack of that error would be
114161

@@ -124,14 +171,15 @@ at App
124171
However, the Owner Stack would only read
125172

126173
```
127-
at SubComponent
128174
at Component
129175
```
130176

131177
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 />`.
132178

133179
Neither `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*/}
@@ -141,30 +189,79 @@ Neither `Navigation` nor `legend` are in the stack at all since it's only a sibl
141189
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.
142190
This 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+
<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;
162223
}
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"
163244
}
164-
);
165-
root.render(<App />);
245+
}
166246
```
167247

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+
168265
## Troubleshooting {/*troubleshooting*/}
169266

170267
### The Owner Stack is `null` {/*the-owner-stack-is-null*/}

0 commit comments

Comments
 (0)