Hook의 동작원리 파헤쳐보기(React 코드 까보기) 02 - 외부 주입 역할을 하는(의존성 관리) ReactSharedInternals.js와 shared 패키지

boyeonJ·2023년 9월 7일
1

React

목록 보기
16/30
post-thumbnail
post-custom-banner

지난번 포스트에서 언급했던 외부 주입 역할을 하는(의존성 관리) ReactSharedInternals.js와 shared 패키지에 대해 자세히 알아보려고 합니다.

우선 전체적으로 hook은 이런 흐름으로 개발자에게 전달됩니다.
1. reconciler ->
2. shared/ReactSharedInternal ->
3. react/ReactSharedInternal ->
4. react/ReactCurrentDispatcher ->
5.react/ReactHooks ->
6. react -> 개발자

이때 shared/ReactSharedInternal, react/ReactSharedInternal는 중간다리 역할을 합니다.

ReactCurrentDispatcher가 포함되어 있는 core의 ReactSharedInternal

core의 ReactSharedInternal.js는 모듈 대기소 라고 할 수 있습니다. 여기 있는 모듈들을 외부에서 주입받기를 대기 하고 있습니다.

주입하는 shared의 ReactSharedInternal

shared 패키지는 공용 폴더 역할을 합니다. 그리고 그 중에서도 실제 ReactSharedInternal에 외부 주입을 진행하는 역할은 ReactSharedInternal.js 가 진행합니다.

core(react)의 ReactSharedInternal.js와 이름이 같다...;;

import React from 'react'

const ReactSharedInternals =
  React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED // core의 ReactSharedInternals.js

if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
  ReactSharedInternals.ReactCurrentDispatcher = {
    current: null,
  }
}
/*...*/

export default ReactSharedInternals

실제 hook을 주입하는 renderWithHooks

실제 hook을 주입하는 역할은 reconciler/renderWithHooks()가 수행합니다. 이 함수는 Render phase에서 실행됩니다.

우선 전체 코드는 아래와 같습니다.

export function renderWithHooks(
  current: Fiber,
  workInProgress: Fiber,
  Component: any,
  props: any,
  refOrContext: any,
  nextRenderExpirationTime: ExpirationTime
) {
  /*...*/
  currentlyRenderingFiber = workInProgress // 현재 작업 중인 fiber를 전역으로 잡아둠
  nextCurrentHook = current !== null ? current.memoizedState : null

  ReactCurrentDispatcher.current =
    nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate

  let children = Component(props, refOrContext)

  /*컴포넌트 재호출 로직..*/

  const renderedWork = currentlyRenderingFiber
  renderedWork.memoizedState = firstWorkInProgressHook

  ReactCurrentDispatcher.current = ContextOnlyDispatcher

 
  currentlyRenderingFiber = null;
   /*...*/
}

가장 먼저 컴포넌트를 호출할는 아래쪽 코드를 살펴보면 memoizedState에 무엇인가를 저장합니다. 이때 저장되는 firstWorkInProgressHook는 전역변수에 저장되어 있는 hook 연결리스트의 head입니다..

그리고 이 memoizedState과 current의 존재 유무로 mount 되어야 하는지 update 되어야 하는지 여부를 판단합니다.

상황별 실제 훅 구현체

위의 코드에 나와있는 실제 훅 구현체 코드는 아래와 같습니다.

// mount
const HooksDispatcherOnMount = {
  useState: mountState,
  useEffect: mountEffect,
  /*...*/
};

// update
const HooksDispatcherOnUpdate: = {
  useState: updateState,
  useEffect: updateEffect,
  /*...*/
};

// invalid hook call
export const ContextOnlyDispatcher: Dispatcher = {
  useState: throwInvalidHookError,
  useEffect: throwInvalidHookError,
  /*...*/
};
post-custom-banner

0개의 댓글