
captureOwnerStack()
markdown
---
title: captureOwnerStack()
---
<Intro>
`captureOwnerStack` 在开发模式下读取当前的所有者堆栈(Owner Stack),并(如果可用)以字符串形式返回。
```js
const stack = captureOwnerStack();
参考 {/reference/}
captureOwnerStack() {/captureownerstack/}
调用 captureOwnerStack 可以获取当前的所有者堆栈。
js {5,5}
import * as React from 'react';
function Component() {
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log(ownerStack);
}
}
参数 {/parameters/}
captureOwnerStack 不接受任何参数。
返回值 {/returns/}
captureOwnerStack 返回 string | null。
所有者堆栈在以下场景中可用:
- 组件渲染过程中
- 副作用(Effects)中(例如
useEffect) - React 事件处理函数中(例如
<button onClick={...} />) - React 错误处理函数中(React Root 选项中的
onCaughtError、onRecoverableError和onUncaughtError)
如果没有可用的所有者堆栈,则返回 null(参见疑难解答:所有者堆栈为 null)。
注意事项 {/caveats/}
- 所有者堆栈仅在开发模式下可用。在非开发模式下,
captureOwnerStack始终返回null。
所有者堆栈与组件堆栈 {/owner-stack-vs-component-stack/}
所有者堆栈与 React 错误处理函数中可用的组件堆栈(如 onUncaughtError 中的 errorInfo.componentStack)不同。
例如,考虑以下代码:
js src/App.js
import {Suspense} from 'react';
function SubComponent({disabled}) {
if (disabled) {
throw new Error('disabled');
}
}
export function Component({label}) {
return (
<fieldset>
<legend>{label}</legend>
<SubComponent key={label} disabled={label === 'disabled'} />
</fieldset>
);
}
function Navigation() {
return null;
}
export default function App({children}) {
return (
<Suspense fallback="loading...">
<main>
<Navigation />
{children}
</main>
</Suspense>
);
}
js src/index.js
import {captureOwnerStack} from 'react';
import {createRoot} from 'react-dom/client';
import App, {Component} from './App.js';
import './styles.css';
createRoot(document.createElement('div'), {
onUncaughtError: (error, errorInfo) => {
// 在控制台中记录堆栈信息,而不是直接在 UI 中展示,以突出
// 浏览器会将 sourcemap 应用于日志中的堆栈。
// 请注意,sourcemap 仅在真实的浏览器控制台中应用,而不是本页面的模拟控制台。
// 点击 "fork" 可以在真实控制台中查看经过 sourcemap 映射的堆栈。
console.log(errorInfo.componentStack);
console.log(captureOwnerStack());
},
}).render(
<App>
<Component label="disabled" />
</App>
);
html public/index.html hidden
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>Check the console output.</p>
</body>
</html>
SubComponent 会抛出一个错误。
该错误的组件堆栈为:
at SubComponent
at fieldset
at Component
at main
at React.Suspense
at App
然而,所有者堆栈只会包含:
at Component
App 和 DOM 组件(例如 fieldset)都不被认为是此堆栈中的所有者,因为它们没有参与“创建”包含 SubComponent 的节点。App 和 DOM 组件只是转发了该节点。App 只是渲染了 children 节点,而 Component 是通过 <SubComponent /> 创建了包含 SubComponent 的节点。
Navigation 和 legend 完全不在堆栈中,因为它们只是包含 <SubComponent /> 节点的兄弟节点。
SubComponent 被省略,因为它已经在调用栈中。
用法 {/usage/}
增强自定义错误覆盖层 {/enhance-a-custom-error-overlay/}
js [[1, 5, "console.error"], [4, 7, "captureOwnerStack"]]
import { captureOwnerStack } from "react";
import { instrumentedConsoleError } from "./errorOverlay";
const originalConsoleError = console.error;
console.error = function patchedConsoleError(...args) {
originalConsoleError.apply(console, args);
const ownerStack = captureOwnerStack();
onConsoleError({
// 请注意,在真实应用中,console.error 可能会被传入多个参数,
// 你需要考虑到这一点。
consoleMessage: args[0],
ownerStack,
});
};
如果你拦截了 console.errorcaptureOwnerStack
css src/styles.css
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
margin: 20px;
padding: 0;
}
h1 {
margin-top: 0;
font-size: 22px;
}
h2 {
margin-top: 0;
font-size: 20px;
}
code {
font-size: 1.2em;
}
ul {
padding-inline-start: 20px;
}
label, button { display: block; margin-bottom: 20px; }
html, body { min-height: 300px; }
#error-dialog {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
padding: 15px;
opacity: 0.9;
text-wrap: wrap;
overflow: scroll;
}
.text-red {
color: red;
}
.-mb-20 {
margin-bottom: -20px;
}
.mb-0 {
margin-bottom: 0;
}
.mb-10 {
margin-bottom: 10px;
}
pre {
text-wrap: wrap;
}
pre.nowrap {
text-wrap: nowrap;
}
.hidden {
display: none;
}
html public/index.html hidden
<!DOCTYPE html>
<html>
<head>
<title>My app</title>
</head>
<body>
<!--
使用原始 HTML 编写的错误对话框,
因为 React 应用中的错误可能导致其崩溃。
-->
<div id="error-dialog" class="hidden">
<h1 id="error-title" class="text-red">Error</h1>
<p>
<pre id="error-body"></pre>
</p>
<h2 class="-mb-20">所有者堆栈:</h4>
<pre id="error-owner-stack" class="nowrap"></pre>
<button
id="error-close"
class="mb-10"
onclick="document.getElementById('error-dialog').classList.add('hidden')"
>
关闭
</button>
</div>
<!-- 这是 DOM 节点 -->
<div id="root"></div>
</body>
</html>
js src/errorOverlay.js
export function onConsoleError({ consoleMessage, ownerStack }) {
const errorDialog = document.getElementById("error-dialog");
const errorBody = document.getElementById("error-body");
const errorOwnerStack = document.getElementById("error-owner-stack");
// 显示 console.error() 消息
errorBody.innerText = consoleMessage;
// 显示所有者堆栈
errorOwnerStack.innerText = ownerStack;
// 显示对话框
errorDialog.classList.remove("hidden");
}
js src/index.js active
import { captureOwnerStack } from "react";
import { createRoot } from "react-dom/client";
import App from './App';
import { onConsoleError } from "./errorOverlay";
import './styles.css';
const originalConsoleError = console.error;
console.error = function patchedConsoleError(...args) {
originalConsoleError.apply(console, args);
const ownerStack = captureOwnerStack();
onConsoleError({
// 请注意,在真实应用中,console.error 可能会被传入多个参数,
// 你需要考虑到这一点。
consoleMessage: args[0],
ownerStack,
});
};
const container = document.getElementById("root");
createRoot(container).render(<App />);
js src/App.js
function Component() {
return <button onClick={() => console.error('Some console error')}>触发 console.error()</button>;
}
export default function App() {
return <Component />;
}
疑难解答 {/troubleshooting/}
所有者堆栈为 null {/the-owner-stack-is-null/}
captureOwnerStack 的调用发生在 React 控制的函数之外,例如在 setTimeout 回调中、在 fetch 调用之后,或者在自定义 DOM 事件处理函数中。在渲染期间、副作用、React 事件处理函数以及 React 错误处理函数(例如 hydrateRoot#options.onCaughtError)中,所有者堆栈是可用的。
在下面的示例中,点击按钮会记录一个空的所有者堆栈,因为 captureOwnerStack 是在自定义 DOM 事件处理函数期间调用的。所有者堆栈必须更早捕获,例如将 captureOwnerStack 的调用移动到 Effect 的主体中。
js
import {captureOwnerStack, useEffect} from 'react';
export default function App() {
useEffect(() => {
// 应该在这里调用 `captureOwnerStack`。
function handleEvent() {
// 在自定义 DOM 事件处理函数中调用就太晚了。
// 此时所有者堆栈将为 `null`。
console.log('所有者堆栈:', captureOwnerStack());
}
document.addEventListener('click', handleEvent);
return () => {
document.removeEventListener('click', handleEvent);
}
})
return <button>点击我,了解所有者堆栈在自定义 DOM 事件处理函数中不可用</button>;
}
captureOwnerStack 不可用 {/captureownerstack-is-not-available/}
captureOwnerStack 仅在开发构建中导出。在生产构建中,它将是 undefined。如果在同时用于生产环境和开发环境打包的文件中使用了 captureOwnerStack,你应该通过命名空间导入来有条件地访问它。
js
// 不要在与开发和生产环境一同打包的文件中使用 `captureOwnerStack` 的命名导入。
import {captureOwnerStack} from 'react';
// 应改用命名空间导入,并有条件地访问 `captureOwnerStack`。
import * as React from 'react';
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log('所有者堆栈', ownerStack);
}
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
