프레임워크 만들기 프로젝트는 처음에는 단지 머리 식히기용으로 시작했지만, 예상치 못한 여정으로 나를 이끌었다. 공부하고 만들면서 어려움에 부딪히고, 끊임없이 개선 아이디어를 고민하며 수 많은 시간을 보냈다. 처음에는 글을 쓰려고 한 것도 아니었지만, 동기부여를 위해 블로그에 글을 쓰게 되었다.
도전 과제들이 많아서 고민이 되지만, 그것이 즐거움(?)의 시작이기도 하다. 사이즈가 큰 과제들이 5개나 되는데, 더 늘어날 가능성까지 염두에 두고 있다. 어떤 것을 먼저 해야할지 고민하면서도 이 모든 것이 나를 설레게(?) 만들고 있디.... 나의 ~갈팡질팡 코딩~ 을 감당 할 수 있게 최대한 변경이 쉽고 확장 가능한 코드를 작성 해야겠다..
React 상태 변화에 따른 렌더링 관련하여 오픈소스를 살펴보면서 느낀 점은, 상태를 immutable
하게 관리해야겠다는 것이었다. 그러나 mutable한 상태 관리 방법에도 공유할 만한 proxy
와 Object.defineProperty()
알찬 메서드가 있는데, 이걸 설명 하지 포스팅 하지 않고 넘어가기에는 너무너무 아쉽다. 다음 포스팅에서는 두 가지 상태 관리 방법에 대해 설명하고자 한다.
블로그를 쓰는 것은 시간이 많이 소요되지만, 아래는 내가 고민하며 무작정 적어둔 글이니, 그저 내 생각을 확인하는 정도로만 읽어주시면 좋겠다.
프레임워크들은 상태를 immutable || mutalble 하게 관리하고 있다.
현 프로젝트에서 mutable한 상태를 관리 하게 된 이유는 불필요한 메모리를 소비 하지 않기 위해 mutable한 상태를 관리하고, 좀더 직관적인 상태 변화를 보고 싶어서 였으나,
개발 하면서 느낀점은 state 하나가 변하면 모든 화면은 다시 그려지고 있음.
jsx를 사용한들 이 문제점은 해결 되지 않음
다시 immutable한 상태를 관리 하기 위해 React의 내부 구현을 참고 함
useState 사용 시
// https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react/src/ReactHooks.js#L23
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
dispatcher.useState
// https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L1767-L1779
function mountState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
* const hook = mountStateImpl(initialState); // React fiber 의 scheduling 관련 로직들이 담겨 있음
const queue = hook.queue;
const dispatch: Dispatch<BasicStateAction<S>> = (dispatchSetState.bind( // 외부로 노출되는 함수로 bind로 각 함수들이 묶여 있음
null,
currentlyRenderingFiber,
queue,
): any);
queue.dispatch = dispatch;
return [hook.memoizedState, dispatch]; // 우리가 만나게 되는 const [state, setState] = useState
}
참고 해야 할 부분
// mountStateImpl- https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L1749C1-L1765C2
// mountWorkInProgressHook - https://github.com/facebook/react/blob/d29f7d973da616a02d6240ea10306a6f33e35ca1/packages/react-reconciler/src/ReactFiberHooks.js#L927-L946
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;
}