유튜브에 React 까보기 시리즈 라는 영상으로 스터디를 진행하며 의미있는 강의는 자주 정리해 보려고 합니다.
강의를 찍으신 시점과 제가 학습하는 시점에 차이가 발생해 React 라이브러리의 코드가 다소 변화되었습니다. 이 글을 읽는 시점에도 코드가 다를 수 있음을 알려 드립니다.
//react/packages/react-reconciler/src/ReactFiberHooks.js
//159번줄
const {ReactCurrentDispatcher, ReactCurrentBatchConfig} = ReactSharedInternals;
renderWithHooks
함수에서 조건에 따라 HooksDispatcherOnMount
와 HooksDispatcherOnUpdate
이 결정된다.//react/packages/react-reconciler/src/ReactFiberHooks.js
//476번줄
export function renderWithHooks<Props, SecondArg>(
...
if (__DEV__) {
if (current !== null && current.memoizedState !== null) {
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
} else if (hookTypesDev !== null) {
// This dispatcher handles an edge case where a component is updating,
// but no stateful hooks have been used.
// We want to match the production code behavior (which will use HooksDispatcherOnMount),
// but with the extra DEV validation to ensure hooks ordering hasn't changed.
// This dispatcher does that.
ReactCurrentDispatcher.current = HooksDispatcherOnMountWithHookTypesInDEV;
} else {
ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
}
} else {
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
}
current === null || current.memoizedState === null
이 된다면 Mount 해야하며, 아닐경우 Update 일경우로 생각//react/packages/react-reconciler/src/ReactFiberHooks.js
//3470번줄
const HooksDispatcherOnMount: Dispatcher = {
readContext,
use,
useCallback: mountCallback,
useContext: readContext,
useEffect: mountEffect,
useImperativeHandle: mountImperativeHandle,
useLayoutEffect: mountLayoutEffect,
useInsertionEffect: mountInsertionEffect,
useMemo: mountMemo,
useReducer: mountReducer,
useRef: mountRef,
useState: mountState,
useDebugValue: mountDebugValue,
useDeferredValue: mountDeferredValue,
useTransition: mountTransition,
useSyncExternalStore: mountSyncExternalStore,
useId: mountId,
};
const HooksDispatcherOnUpdate: Dispatcher = {
readContext,
use,
useCallback: updateCallback,
useContext: readContext,
useEffect: updateEffect,
useImperativeHandle: updateImperativeHandle,
useInsertionEffect: updateInsertionEffect,
useLayoutEffect: updateLayoutEffect,
useMemo: updateMemo,
useReducer: updateReducer,
useRef: updateRef,
useState: updateState,
useDebugValue: updateDebugValue,
useDeferredValue: updateDeferredValue,
useTransition: updateTransition,
useSyncExternalStore: updateSyncExternalStore,
useId: updateId,
};
//react/packages/react-reconciler/src/ReactFiberHooks.js
//476번줄
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber ... ): any {
renderLanes = nextRenderLanes;
**currentlyRenderingFiber = workInProgress; <- 이 코드가 핵심**
updateWorkInProgressHook
함수에서 사용=== null || current.memoizedState === null
로 대체된다.// 572번
let children = Component(props, secondArg);
// **업데이트 정보를 스케쥴러와 패키지에게 전달 했음?을 확인 -> Mount 일경우는 false**
if (didScheduleRenderPhaseUpdateDuringThisPass) {
// Keep rendering until the component stabilizes (there are no more render
// phase updates).
children = renderWithHooksAgain(
workInProgress,
Component,
props,
secondArg,
);
}
// 607번
function finishRenderingHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
): void {
// **이것을 왜?**
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
const didRenderTooFewHooks =
currentHook !== null && currentHook.next !== null;
renderLanes = NoLanes;
currentlyRenderingFiber = (null: any);
currentHook = null;
workInProgressHook = null;
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
이 코드가 의미하는 것은 ReactCurrentDispatcher.current를 재할당 하는 것이 아니라 위에서 컴포넌트를 호출한 뒤 더 이상 Hook을 요청해서는 안될때 Error를 알려주기 위함을 의미//3432번줄
export const ContextOnlyDispatcher: Dispatcher = {
readContext,
use,
useCallback: throwInvalidHookError,
useContext: throwInvalidHookError,
useEffect: throwInvalidHookError,
useImperativeHandle: throwInvalidHookError,
useInsertionEffect: throwInvalidHookError,
useLayoutEffect: throwInvalidHookError,
useMemo: throwInvalidHookError,
useReducer: throwInvalidHookError,
useRef: throwInvalidHookError,
useState: throwInvalidHookError,
useDebugValue: throwInvalidHookError,
useDeferredValue: throwInvalidHookError,
useTransition: throwInvalidHookError,
useSyncExternalStore: throwInvalidHookError,
useId: throwInvalidHookError,
};
function throwInvalidHookError() {
throw new Error(
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
' one of the following reasons:\n' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
'2. You might be breaking the Rules of Hooks\n' +
'3. You might have more than one copy of React in the same app\n' +
'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
);
}
// 926번줄
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
// This is the first hook in the list
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
// Append to the end of the list
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
// 1750번
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
const initialStateInitializer = initialState;
// $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
initialState = initialStateInitializer();
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
// $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
initialStateInitializer();
setIsStrictModeForDevtools(false);
}
}
hook.memoizedState = hook.baseState = initialState;
const queue: UpdateQueue<S, BasicStateAction<S>> = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
};
hook.queue = queue;
return hook;
}
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountStateImpl(initialState);
const queue = hook.queue;
const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
): any);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
///607 번줄
function finishRenderingHooks<Props, SecondArg>(
current: Fiber | null,
workInProgress: Fiber,
Component: (p: Props, arg: SecondArg) => any,
): void {
if (__DEV__) {
workInProgress._debugHookTypes = hookTypesDev;
}
// We can assume the previous dispatcher is always this one, since we set it
// at the beginning of the render phase and there's no re-entrance.
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
// This check uses currentHook so that it works the same in DEV and prod bundles.
// hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
const didRenderTooFewHooks =
currentHook !== null && currentHook.next !== null;
renderLanes = NoLanes;
currentlyRenderingFiber = (null: any);
currentHook = null;
workInProgressHook = null;
Wow, cool post. I’d like to write like this too – taking time and real hard work to make a great article… but I put things off too much and never seem to get started. Thanks though. สล็อตเว็บตรง
Wow, cool post. I’d like to write like this too – taking time and real hard work to make a great article… but I put things off too much and never seem to get started. Thanks though. esports lawyers
I was reading some of your content on this website and I conceive this internet site is really informative ! Keep on putting up.olxtoto loginSkrotbilarnaBlanca City by Sun Grouphttps://www.sshh.uk.com/toto macauolxtoto login
Thanks for the blog loaded with so many information. Stopping by your blog helped me to get what I was looking for.toto macau hari iniagenolx link alternatifWorld Cup betting Californiaonline casinos in Californialawn sprinkler repair servicedallas commercial pest control
This is my first visit to your web journal! We are a group of volunteers and new activities in the same specialty. Website gave us helpful data to work.bandar toto online
Hey what a brilliant post I have come across and believe me I have been searching out for this similar kind of post for past a week and hardly came across this. Thank you very much and will look for more postings from you.link gacor
When your website or blog goes live for the first time, it is exciting. That is until you realize no one but you and your.Chikii Cloud Game
Keep up the good work; I read few posts on this website, including I consider that your blog is fascinating and has sets of the fantastic piece of information. Thanks for your valuable efforts.situs slot gacor
Took me time to read all the comments, but I really enjoyed the article. It proved to be Very helpful to me and I am sure to all the commenters here! It’s always nice when you can not only be informed, but also entertained!วิเคราะห์บอล 7m
Good website! I truly love how it is easy on my eyes it is. I am wondering how I might be notified whenever a new post has been made. I have subscribed to your RSS which may do the trick? Have a great day!koitoto
Great post but I was wondering if you could write a little more on this subject? I’d be very thankful if you could elaborate a little bit further. Thanks in advanceolxtoto linkkoitotototo togellawn sprinkler repair servicekoitoto
I am incapable of reading articles online very often, but I’m happy I did today. It is very well written, and your points are well-expressed. I request you warmly, please, don’t ever stop writing. Satta king