captureOwnerStack

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는 매개변수를 받지 않습니다.

반환값

captureOwnerStackstring이나 null을 반환합니다.

Owner Stacks은 다음 경우에 사용할 수 있습니다.

  • 컴포넌트 렌더링 시
  • 이펙트 (예: useEffect)
  • React 이벤트 핸들러 (예: <button onClick={...} />)
  • React 오류 핸들러 (React 루트 옵션 onCaughtError, onRecoverableError, onUncaughtError)

Owner Stack을 사용할 수 없는 경우, null이 반환됩니다. (문제해결: Owner Stack이 null인 경우) 3

주의 사항

  • Owner Stack은 개발 환경에서만 사용할 수 있습니다. captureOwnerStack은 개발 환경 밖에서는 항상 null을 반환합니다.
자세히 살펴보기

Owner Stack vs Component Stack

Owner Stack은 errorInfo.componentStack in onUncaughtError와 같은 리액트 오류 핸들러에서 사용할 수 있는 Component Stack과 다릅니다.

예를 들어 다음 코드를 살펴보겠습니다.

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) => {
    // The stacks are logged instead of showing them in the UI directly to
    // highlight that browsers will apply sourcemaps to the logged stacks.
    // Note that sourcemapping is only applied in the real browser console not
    // in the fake one displayed on this page.
    // Press "fork" to be able to view the sourcemapped stack in a real console.
    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)은 SubComponent를 포함하는 노드를 “생성하는” 데에 기여하지 않기 때문 이 스택에 포함되지 않습니다. App과 DOM 컴포넌트들은 노드를 전달할 뿐입니다. App<SubComponent />를 통해 SubComponent를 포함한 노드를 생성하는 Component와 달리 children 노드만 렌더링합니다.

Navigationlegend<SubComponent />를 포함하는 노드의 형제 요소이기 때문에 스택에 전혀 포함되지 않습니다.

SubComponent는 이미 호출 스택에 포함되어 있기 떄문에 Owner Stack에 나타나지 않습니다.

사용법

커스텀 오류 오버레이 개선하기

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({
// Keep in mind that in a real application, console.error can be
// called with multiple arguments which you should account for.
consoleMessage: args[0],
ownerStack,
});
};

console.error 호출을 가로채서 오류 오버레이에 표시하고 싶다면, captureOwnerStack을 호출하여 OwnerStack을 포함할 수 있습니다.

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({
    // Keep in mind that in a real application, console.error can be
    // called with multiple arguments which you should account for.
    consoleMessage: args[0],
    ownerStack,
  });
};

const container = document.getElementById("root");
createRoot(container).render(<App />);

문제 해결

Owner Stack이 null인 경우

captureOwnerStack이 React가 제어하지 않는 함수 바깥에서 호출됐을 경우, 예를 들어 setTimeout 콜백, fetch 호출 후, 커스텀 DOM 이벤트 핸들러 등에서는 Owner Stack이 null이 됩니다. 렌더링 중이나 이펙트, React 이벤트 핸들러, React 오류 핸들러(예: hydrateRoot#options.onCaughtError) 내에서만 생성됩니다.

아래 예시에서, 버튼을 클릭하면 빈 Owner Stack이 로그로 출력됩니다. 그 이유는 captureOwnerStack이 커스텀 이벤트 핸들러 내에서 호출되었기 때문입니다. Owner Stack은 더 이른 시점, 예를 들어 이펙트 내부에서 captureOwnerStack를 호출하도록 이동시켜야 올바르게 캡처할 수 있습니다.

import {captureOwnerStack, useEffect} from 'react';

export default function App() {
  useEffect(() => {
    // Should call `captureOwnerStack` here.
    function handleEvent() {
      // Calling it in a custom DOM event handler is too late.
      // The Owner Stack will be `null` at this point.
      console.log('Owner Stack: ', captureOwnerStack());
    }

    document.addEventListener('click', handleEvent);

    return () => {
      document.removeEventListener('click', handleEvent);
    }
  })

  return <button>Click me to see that Owner Stacks are not available in custom DOM event handlers</button>;
}

captureOwnerStack을 사용할 수 없는 경우

captureOwnerStack은 개발 환경 빌드에서만 export됩니다. 프로덕션 환경 빌드에서는 undefined입니다. captureOwnerStack이 개발과 프로덕션이 모두 번들링되는 파일에서 사용될 때는 네임스페이스 import를 사용하고 조건부로 접근해야 합니다.

// Don't use named imports of `captureOwnerStack` in files that are bundled for development and production.
import {captureOwnerStack} from 'react';
// Use a namespace import instead and access `captureOwnerStack` conditionally.
import * as React from 'react';

if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log('Owner Stack', ownerStack);
}