BlogReactReconcilerWorkTags

React-reconciler | WorkTags (19.0.0)

React의 Reconciliation 과정에서 사용되는 내부 구현 개념입니다.

  • 각 fiber node의 유형을 식별
  • React가 컴포넌트를 어떻게 처리해야 할지 결정
  • 렌더링 최적화를 위한 정보 제공
react/packages/react-reconciler/src/ReactWorkTags.js
export type WorkTag = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29;
 
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const HostRoot = 3; 
export const HostPortal = 4;
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const ScopeComponent = 21;
export const OffscreenComponent = 22;
export const LegacyHiddenComponent = 23;
export const CacheComponent = 24;
export const TracingMarkerComponent = 25;
export const HostHoistable = 26;
export const HostSingleton = 27;
export const IncompleteFunctionComponent = 28;
export const Throw = 29;

주요 Work Tags 이해하기

FunctionComponent

함수형 컴포넌트를 의미합니다.

function Example() {
  return <div>Hello</div>;
}

ClassComponent

클래스형 컴포넌트를 의미합니다.

class Example extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}

HostRoot

ReactDOM.render()의 두번째 인자로 들어오는 root입니다.

ReactDOM.render(<App />, document.getElementById('root'));

HostPortal

ReactDOM.createPortal()의 두번째 인자로 들어오는 container입니다.

ReactDOM.createPortal(<div>Hello</div>, document.body);

HostComponent

div, span, p 등 DOM 요소를 의미합니다.

<div>Hello</div>

HostText

string | number타입이며 "Hello", 1HostText입니다.

<div>Hello</div>
<div>1</div>

Fragment

자식을 그룹화할 수 있습니다.

<>
  <div>Hello 1</div>
  <div>Hello 2</div>
</>
 
<React.Fragment>
  <div>Hello 1</div>
  <div>Hello 2</div>
</React.Fragment>

Mode

<React.StrictMode>
  <div>Hello</div>
</React.StrictMode>

ContextConsumer

Context value를 사용하는 방법을 제공합니다.

<MyContext.Consumer>
  {value => (<div>{value}</div>)}
</MyContext.Consumer>

ContextProvider

Context value를 사용하는 방법을 제공합니다.

<MyContext.Provider value={value}>
  <App />
</MyContext.Provider>

ForwardRef

ref를 전달하는 컴포넌트를 만드는 데 사용됩니다. 주로 하위 컴포넌트의 DOM 노드나 인스턴스에 접근할 때 유용합니다.

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="fancy">
    {props.children}
  </button>
));

Profiler

성능 측정을 위해 특정 컴포넌트의 렌더링 시간 등을 기록합니다.

<Profiler id="App" onRender={callback}>
  <App />
</Profiler>

SuspenseComponent

비동기 작업(data fetching, module import 등)이 완료될 때까지 fallback UI를 표시합니다.

<Suspense fallback={<Loading />}>
  <LazyComponent />
</Suspense>

MemoComponent

memo 기능을 사용하면서 직접 비교 로직을 구현한 경우입니다.

const MemoizedComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
}, (prevProps, nextProps) => {
  return prevProps.value === nextProps.value;
});

SimpleMemoComponent

memo 기능을 사용하면서 직접 비교 로직을 구현하지 않은 경우입니다.

const MemoizedComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
});
react/packages/react-Reconciler/src/ReactFiberBeginWorks.js > updateMemoComponent > 477 Line
function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
): null | Fiber {
  if (current === null) {
    const type = Component.type;
    if (
      isSimpleFunctionComponent(type) &&
      Component.compare === null &&
      // SimpleMemoComponent codepath doesn't resolve outer props either.
      (disableDefaultPropsExceptForClasses ||
        Component.defaultProps === undefined)
    ) {
      let resolvedType = type;
      if (__DEV__) {
        resolvedType = resolveFunctionForHotReloading(type);
      }
      // If this is a plain function component without default props,
      // and with only the default shallow comparison, we upgrade it
      // to a SimpleMemoComponent to allow fast path updates.
      workInProgress.tag = SimpleMemoComponent;
      ...
    }
  }
  ...
}

LazyComponent

동적 임포트를 위한 lazy 기능이 적용된 컴포넌트입니다.

const LazyComponent = React.lazy(() => import('./LazyComponent'));

DehydratedFragment

서버 사이드 렌더링(SSR)에서 클라이언트로 전달된 초기 상태를 React 앱이 재활성화하는 데 사용되는 것을 의미합니다.

import { Hydrate, DehydratedState } from 'react-query/hydration';
import { QueryClient, QueryClientProvider } from 'react-query';
 
export async function getStaticProps() {
  const queryClient = new QueryClient();
  
  // 데이터 미리 가져오기
  await queryClient.prefetchQuery('posts', getPosts);
  
  return {
    props: {
      // 데이터를 직렬화하여 클라이언트로 전달
      dehydratedState: dehydrate(queryClient),
    },
  };
}
 
function App({ dehydratedState }: { dehydratedState: DehydratedState }) {
  const queryClient = new QueryClient();
 
  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={dehydratedState}>
        <MyComponent />
      </Hydrate>
    </QueryClientProvider>
  );
}

SuspenseListComponent

여러 Suspense 컴포넌트의 로딩 순서를 조정합니다.

<SuspenseList>
  <Suspense fallback={<Loading1 />}>
    <LazyComponent1/>
  </Suspense>
  <Suspense fallback={<Loading2 />}>
    <LazyComponent2/>
  </Suspense>
<SuspenseList/>

Throw

잘못된 Element type을 가진 React Component를 렌더링하려 할 때 발생하는 예외를 처리합니다.

react/packages/react-Reconciler/src/ReactFiber.js > createFiberFromTypeAndProps > 722 line
// The type is invalid but it's conceptually a child that errored and not the
// current component itself so we create a virtual child that throws in its
// begin phase. This is the same thing we do in ReactChildFiber if we throw
// but we do it here so that we can assign the debug owner and stack from the
// element itself. That way the error stack will point to the JSX callsite.
fiberTag = Throw;
pendingProps = new Error(
  'Element type is invalid: expected a string (for built-in ' +
    'components) or a class/function (for composite components) ' +
    `but got: ${typeString}.${info}`,
);

참고 자료

React | react-reconciler