Open j03y14 opened 1 year ago
useState 위치: https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js#L96
const dispatcher = resolveDispatcher();
return dispatcher.useReducer(reducer, initialArg, init);
const dispatcher = ReactCurrentDispatcher.current;
if (__DEV__) {
if (dispatcher === null) {
console.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://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
);
}
}
// Will result in a null access error if accessed outside render phase. We
// intentionally don't throw our own error because this is in a hot path.
// Also helps ensure this is inlined.
return ((dispatcher: any): Dispatcher);
invalid hook call 에러가 dispatcher가 없으면 나는 에러였구나. hook 규칙에 안 맞으면 null을 리턴하게 되어있겠네.
import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
/**
* Keeps track of the current dispatcher.
*/
const ReactCurrentDispatcher = {
current: (null: null | Dispatcher),
};
export default ReactCurrentDispatcher;
current에 언제 넣어주는건데? [[react-fiber]]
function updateFunctionComponent() {
...
nextChildren = renderWithHooks(
current,
workInProgress,
Component,
nextProps,
context,
renderLanes,
);
...
}
ReactCurrentDispatcher.current =
current === null || current.memoizedState === null
? HooksDispatcherOnMount
: HooksDispatcherOnUpdate;
결국 HooksDispatcherOnMount
이거나 HooksDispatcherOnUpdate
이거 봐야됨
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,
};
useState는 mountState. 얘는 함수이다.
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const hook = mountStateImpl(initialState); // hook은 각각의 객체
const queue = hook.queue;
const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind(
null,
currentlyRenderingFiber,
queue,
): any);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch];
}
state: hook.memoizedState, setState는 dispatch는.
https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.js#L1727
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
// $FlowFixMe[incompatible-use]: Flow doesn't like mixed types
initialState = initialState();
}
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 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;
}
workInProgressHook이 global 하게 let 변수로 저장이 되어있음.
mountWorkInProgressHook에서 next로 훅의 순서를 알 수 있는 것이었음.
궁금한점