react package 구성요소
react core
- component정의
- 다른 package에 의존성이 없기 때문에 다양한 플랫폼에 사용가능
renderer
- react-dom, react-native-renderer등 host rendering환경에 의존
- host와 react를 연결하여 Web의 DOM을 조작
- reconciler와 legacy-events package의존성
legacy-events
- 기존 web에서 event를 wrapping하여 추가적인 기능을 수행
- syntheticevent(합성이벤트)라는 event system
scheduler
reconciler(재조정)
- fiber architecture에서 VDOM(Virtual DOM)을 재조정하는 담당
- Component를 호출하는 곳
react 용어
react rendering
- 컴포넌트를 호출하여 return react element > VDOM에 적용(재조정)하는 과정
과정
- 컴포넌트 호출 return react element
- VDOM재조정 진행
- renderer가 컴포넌트 정보를 DOM에 삽입 - mount
- 브라우저가 DOM을 paint(다시 그리기)
react element
- 컴포넌트 호출 시 return 진행(JSX > Babel을 통해서 react.createElement()호출)
- 컴포넌트의 정보(DOM에 삽입될 내용)을 담은 객체 - type,key,props,ref등의 정보
fiber
- VDOM의 노드 객체(architecture 이름과 동일)
- react element의 내용이 DOM에 반영되기 위해, 먼저 VDOM에 추가되어야 하기 때문에 확장한 객체 - 컴포넌트 상태, life-cycle, hook
VDOM(Virtual DOM) and React lifecycle
VDOM란?
메모리 상에 UI관련된 정보를 띄우고, react-dom과 같은 라이브러리에 의해 실제 DOM과 연결(renderer 관여)하여 재조정진행 (재조정은 reconciler관여)
가상환경을 사용하는 이유는 실제 DOM을 mount하고 다시 paint하는 과정에서 발생하는 비용이 가상보다 크기 때문
VDOM 구성
- fiber node로 구성된 형태
- current (DOM에 mount된 fiber)
- workInProgress (render phase에서 작업 중인 fiber, commit phase를 지나면서 current tree가 됨.)
- workInProgress tree는 current tree에서 자기 복제하여 만들어짐.
(서로 alternate참조)
- fiber는 첫번째 자식만을 child로 참조, 나머지 자식들은 서로 sibling으로 참조
- 모든 자식은 부모를 return으로 참조
- 컴포넌트 리렌더링 : 컴포넌트 호출 후 그 결과가 VDOM에 반영되지만, DOM에 mount되어 paint안됨.
React lifecycle
1. render phase
- VDOM 재조정(reconciliation)하는 단계
- element추가, 수정, 삭제 -> WORK를 scheduler에 등록
- WORK는 reconciler가 컴포넌트의 변경을 DOM에 적용하기 위해 수행
- reconciler가 담당(reconciler설계 stack > fiber를 바뀌면서 abort, stop, restart)\
즉, 렌더링 우선순위 변경 가능 useTransition등
2. commit phase
- 재조정한 VDOM을 DOM에 적용 & 라이프사이클 실행하는 단계
- 일관성을 위해 연결 진행, DOM조작 일괄 처리 후 > 리액트가 callstack을 비워준 다음 브라우저가 paint시작
React Hook
- react core : ReactCurrentDispatcher > ReactHoooks > React
- react 코어는 react element에 대한 정보만 알고 있다.
- react element는 fiber로 확장해야 hook을 포함한다.
- react 코어는 hook을 사용하기 위해 외부에서 주입받는다.
이유 : 의존성을 끊고 여러 곳에서 사용하기 위함
- shared는 모든 패키지가 공유하는 공통 패키지
- shared/ReactSharedInternals.js는 react코어 패키지에 연결된 출입구
- reconciler는 shared에 hook을 주입(객체에 속성으로 할당)
useState
- 순서
reconciler > shared/ReactSharedInternal > react/ReactSharedInternal > react/ReactCurrentDispatcher > react/ReactHooks > react > 개발자
reconciler의 renderWithHooks()에서는 무슨일 있을까?
useState export방식
- nextCurrentHook이 null O > mount (HooksDispatcherOnMount)
- nextCurrentHook이 null X > update(HooksDispatcherOnUpdate)
- HooksDispatcherOnMount 객체에 할당한 mountState
renderWithHooks
- renderWithHooks() > hook과 함께 render 즉, hook을 주입하는 역할
- 렌더링 : 컴포넌트 호출 후 그 결과가 VDOM에 반영되는 과정
- 컴포넌트 상태가 mount, update일 때, 다음 컴포넌트가 사용할 수 있게 전역변수 초기화 로직도 포함한다.
- nextCurrentHook, firstWorkInProgressHook 등 전역변수 다시 말해 작업 중인 컴포넌트에만 접근가능한 값
- 작업(hook주입, 렌더링 등)이 끝나면, null로 초기화해서 다음 컴포넌트가 활용할 수 있도록 준비하는 로직
- 함수가 다 끝나기 전에 다른 함수를 호출 그 함수가 끝나기 전 함수에서 활용하던 변수를 사용해야하고 전역변수로도 활용이 가능하다
Component(fiber)와 hook연결
- currentlyRenderingFiber전역변수에 workInProgress할당
nextCurrentHook
- current가 null X -> current.memoizedState할당
- current가 null O -> null할당
- current는 VDOM중에서 DOM에 mount된 정보를 가진 fiber
(작업중 fiber = workInProgress)
- current.memoizedState에는 hook이 들어가 있다.
(fiber의 memoizedState -> hook할당 )
ReactCurrentDispatcher가 위에 할당이 되었음에도 다시 할당을 진행하는 것을 볼 수 있다. component에 할당하는 과정에서 error을 대처하기 위해 ContextOnlyDispatcher를 할당 함으로써 throwInvalidHookError을 진행하는 것을 볼 수 있다.
useState를 호출하면 이제 mountState가 호출된다.
호출된 mountState에서는 mountWorkInProgressHook이 가장 처음에 불러진다.