useState 완전분석(feat. Linked list)
function mountWorkInProgressHook(): Hook {
const hook: Hook = {
memoizedState: null,
baseState: null,
baseQueue: null,
queue: null,
next: null,
};
if (workInProgressHook === null) {
currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
workInProgressHook = workInProgressHook.next = hook;
}
return workInProgressHook;
}
- memoizedState 는 상태가 변한 후 최종적으로 기억되는 값
- next 는 링크드리스트 처럼 연결되어 있다. 이 값은 다음 훅을 할당될 키값 이다.
- fiber는 hook이 링크드리스트로 연결되어 있다.
mountStateImpl
function mountStateImpl<S>(initialState: (() => S) | S): Hook {
const hook = mountWorkInProgressHook();
if (typeof initialState === 'function') {
const initialStateInitializer = initialState;
initialState = initialStateInitializer();
if (shouldDoubleInvokeUserFnsInHooksDEV) {
setIsStrictModeForDevtools(true);
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;
}
- 이곳에서 initialState가 함수이면 함수를 실행시킨 값을 전달함을 알 수 있다.
Hook에서 왜 queue 객체를 사용할까?
const queue: UpdateQueue<S, BasicStateAction<S>> = {
pending: null,
lanes: NoLanes,
dispatch: null,
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
};
- pending은 업데이트 될 다음 상태를 나타낸다.
- dispatch는 queue에서 추가로 들어갈 수 있도록 도와주는 것
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];
}
- dispatch는 이전에 선언했던 queue가 바인딩 되고 있다.
- 이 dispatch는 외부로 노출되고 있다. → 이것이 결론적으로 setState