React-reconciler | WorkTags (19.0.0)
React의 Reconciliation 과정에서 사용되는 내부 구현 개념입니다.
- 각 fiber node의 유형을 식별
- React가 컴포넌트를 어떻게 처리해야 할지 결정
- 렌더링 최적화를 위한 정보 제공
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"
, 1
이 HostText
입니다.
<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>;
});
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를 렌더링하려 할 때 발생하는 예외를 처리합니다.
// 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}`,
);