captureOwnerStack 在开发环境中读取当前的 Owner Stack,并在可用时将其作为字符串返回。
const stack = captureOwnerStack();参考
captureOwnerStack()
调用 captureOwnerStack 以获取当前的 Owner Stack。
import * as React from 'react';
function Component() {
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log(ownerStack);
}
}参数
captureOwnerStack 不接受任何参数。
返回值
captureOwnerStack 返回 string | null。
Owner Stacks 可在以下场景中使用:
- 组件渲染
- Effects(例如
useEffect) - React 的事件处理器(例如
<button onClick={...} />) - React 错误处理器(React Root options 中的
onCaughtError、onRecoverableError和onUncaughtError)
如果没有可用的 Owner Stack,则返回 null(参见 故障排查:Owner Stack 为 null)。
注意事项
- Owner Stacks 仅在开发环境中可用。
captureOwnerStack在开发环境之外始终返回null。
Deep Dive
Owner Stack 与 React 错误处理器中可用的 Component Stack 不同,例如 onUncaughtError 中的 errorInfo.componentStack。
例如,考虑以下代码:
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> );
SubComponent 会抛出错误。
该错误的 Component Stack 将为:
at SubComponent
at fieldset
at Component
at main
at React.Suspense
at App然而,Owner Stack 只会显示:
at Component在这个堆栈中,App 和 DOM 组件(例如 fieldset)都不被视为 Owner,因为它们并未参与“创建”包含 SubComponent 的节点。App 和 DOM 组件只是传递了该节点。App 只是渲染了 children 节点,而 Component 则通过 <SubComponent /> 创建了一个包含 SubComponent 的节点。
Navigation 和 legend 根本不会出现在堆栈中,因为它们只是包含 <SubComponent /> 的节点的兄弟节点。
SubComponent 被省略了,因为它已经是调用栈的一部分。
用法
增强自定义错误覆盖层
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.error 调用并在错误覆盖层中高亮它们,那么你可以调用 captureOwnerStack 来包含 Owner Stack。
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 />);
故障排查
Owner Stack 为 null
调用 captureOwnerStack 发生在 React 控制的函数之外,例如在 setTimeout 回调中、fetch 调用之后,或者在自定义 DOM 事件处理器中。在渲染、Effects、React 事件处理器以及 React 错误处理器(例如 hydrateRoot#options.onCaughtError)期间,Owner Stack 应该是可用的。
在下面的示例中,点击按钮会记录一个空的 Owner Stack,因为 captureOwnerStack 是在自定义 DOM 事件处理器中调用的。必须更早地捕获 Owner Stack,例如将 captureOwnerStack 的调用移动到 Effect 主体中。
import {captureOwnerStack, useEffect} from 'react'; export default function App() { useEffect(() => { // 应该在这里调用 `captureOwnerStack`。 function handleEvent() { // 在自定义 DOM 事件处理器中调用它已经太晚了。 // 此时 Owner Stack 将为 `null`。 console.log('Owner Stack: ', captureOwnerStack()); } document.addEventListener('click', handleEvent); return () => { document.removeEventListener('click', handleEvent); } }) return <button>点击我以查看在自定义 DOM 事件处理器中不可用的 Owner Stack</button>; }
captureOwnerStack 不可用
captureOwnerStack 只会在开发构建中导出。在生产构建中它将是 undefined。如果 captureOwnerStack 被用于同时会打包到生产和开发环境的文件中,你应该通过命名空间导入有条件地访问它。
// 不要在同时会被打包用于开发和生产的文件中使用 `captureOwnerStack` 的命名导入。
import {captureOwnerStack} from 'react';
// 而是改用命名空间导入,并有条件地访问 `captureOwnerStack`。
import * as React from 'react';
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log('Owner Stack', ownerStack);
}