useEffect를 사용할 때 보통 이렇게 작성한다.
useEffect(() => {
// ...
return () => {
// cleanup..
}
}, [deps])
우리가 컴포넌트에서 useEffect를 호출하면, React는 내부적으로 여러 단계를 거쳐 Effect 객체를 생성하고 관리한다.
먼저 우리가 호출하는 useEffect는 이 파일에서 시작된다.
앞으로 파일 경로가 계속 나오게 될텐데 facebook/react의 repository를 참고하면 된다.
`packages/react/src/ReactHooks.js`
export function useEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const dispatcher = resolveDispatcher();
return dispatcher.useEffect(create, deps);
}
이 함수는 현재 React의 dispatcher를 확인하고, 그에 맞는 useEffect 구현체를 호출한다. dispatcher는 React가 현재 렌더링 상태에 따라 적절한 구현체를 선택하는 데 사용된다.
dispatcher에서 선택된 구현체는 컴포넌트의 마운트 여부에 따라 mountEffect 또는 updateEffect를 호출한다.
최초 마운트 시
`packages/react-reconciler/src/ReactFiberHooks.js`
function mountEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
if (
__DEV__ &&
(currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode &&
(currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode
) {
mountEffectImpl(
MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
} else {
mountEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
deps,
);
}
}
function mountEffectImpl(
fiberFlags: Flags,
hookFlags: HookFlags,
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushSimpleEffect(
HookHasEffect | hookFlags,
createEffectInstance(),
create,
nextDeps,
);
}
업데이트 시
`packages/react-reconciler/src/ReactFiberHooks.js` (최초 마운트랑 같은 파일이다!)
function updateEffect(
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
updateEffectImpl(PassiveEffect, HookPassive, create, deps);
}
function mountResourceEffect(
create: () => mixed,
createDeps: Array<mixed> | void | null,
update: ((resource: mixed) => void) | void,
updateDeps: Array<mixed> | void | null,
destroy: ((resource: mixed) => void) | void,
) {
if (
__DEV__ &&
(currentlyRenderingFiber.mode & StrictEffectsMode) !== NoMode &&
(currentlyRenderingFiber.mode & NoStrictPassiveEffectsMode) === NoMode
) {
mountResourceEffectImpl(
MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
createDeps,
update,
updateDeps,
destroy,
);
} else {
mountResourceEffectImpl(
PassiveEffect | PassiveStaticEffect,
HookPassive,
create,
createDeps,
update,
updateDeps,
destroy,
);
}
}
function updateEffectImpl(
fiberFlags: Flags,
hookFlags: HookFlags,
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): void {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const effect: Effect = hook.memoizedState;
const inst = effect.inst;
// currentHook is null on initial mount when rerendering after a render phase
// state update or for strict mode.
if (currentHook !== null) {
if (nextDeps !== null) {
const prevEffect: Effect = currentHook.memoizedState;
const prevDeps = prevEffect.deps;
// $FlowFixMe[incompatible-call] (@poteto)
if (areHookInputsEqual(nextDeps, prevDeps)) {
hook.memoizedState = pushSimpleEffect(
hookFlags,
inst,
create,
nextDeps,
);
return;
}
}
}
currentlyRenderingFiber.flags |= fiberFlags;
hook.memoizedState = pushSimpleEffect(
HookHasEffect | hookFlags,
inst,
create,
nextDeps,
);
}
여기서 pushEffect는 실제 Effect 객체를 생성하고 이를 fiber의 updateQueue에 추가한다.
function pushSimpleEffect(
tag: HookFlags,
inst: EffectInstance,
create: () => (() => void) | void,
deps: Array<mixed> | void | null,
): Effect {
const effect: Effect = {
tag,
create,
deps,
inst,
// Circular
next: (null: any),
};
return pushEffectImpl(effect);
}
생성된 Effect 객체는 컴포넌트의 fiber 노드에 연결되어 나중에 커밋 단계에서 실행된다. 이 실행은 commitRoot 함수에서 시작되어 flushPassiveEffects를 통해 비동기적으로 처리된다.
위와 같은 단계적인 처리를 통해 React는 effect의 생성, 의존성 체크, 실행 타이밍, 정리(cleanup) 등을 관리할 수 있다.
references
https://ko.react.dev/reference/react/useEffect
https://jser.dev/2023-07-08-how-does-useeffect-work/
https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js
https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.js
'Web, Front-end' 카테고리의 다른 글
[Javascript] 브라우저 팝업 차단으로 인한 문제와 해결책 (0) | 2024.12.01 |
---|---|
[Typescript] 사진과 영상을 FormData로 서버에 전송하기 (6) | 2024.09.19 |
[Javascript] 이벤트 전파 (1) | 2024.08.27 |
[Javascript] 이벤트 핸들러 등록 (0) | 2024.08.27 |
[Javascript] const 키워드 (0) | 2024.06.05 |
컴퓨터 전공 관련, 프론트엔드 개발 지식들을 공유합니다. React, Javascript를 다룰 줄 알며 요즘에는 Typescript에도 관심이 생겨 공부하고 있습니다. 서로 소통하면서 프로젝트 하는 것을 즐기며 많은 대외활동으로 개발 능력과 소프트 스킬을 다듬어나가고 있습니다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!