Summary#
React useEffect 안에서 시작한 async 작업은 component lifecycle보다 늦게 완료될 수 있다. React 18에서는 과거의 “unmounted component에 state update” 경고가 제거되었지만, stale result가 최신 UI를 덮어쓰거나 불필요한 network work가 계속되는 문제는 여전히 남는다. Effect에는 명시적인 cleanup 경로를 두고, async 완료 시점에는 cancellation 또는 ignore/race guard를 확인한다.
Problem#
useEffect에서 fetch, timer, subscription, 외부 callback 같은 async 작업을 시작하면 다음 상황이 생긴다.
- component가 unmount된 뒤 promise가 resolve된다.
- dependency가 바뀌어 새 요청이 시작됐는데 이전 요청이 나중에 resolve되어 최신 상태를 덮어쓴다.
- Strict Mode 개발 환경에서 setup/cleanup이 한 번 더 실행되어 cleanup 누락이 더 쉽게 드러난다.
- React 17 이하에서는 “Can't perform a React state update on an unmounted component” 류 경고가 보일 수 있었지만, React 18에서는 이 경고가 제거되어 문제를 warning 유무로만 판단하면 안 된다.
Recommended Pattern#
가능하면 async 작업 자체를 취소하고, 취소만으로 충분하지 않은 race에는 ignore flag 또는 request id guard를 함께 둔다.
useEffect(() => {
const controller = new AbortController();
let ignore = false;
async function load() {
try {
const res = await fetch(url, { signal: controller.signal });
const data = await res.json();
if (!ignore) {
setData(data);
}
} catch (err) {
if (!ignore && !(err instanceof DOMException && err.name === 'AbortError')) {
setError(err);
}
}
}
load();
return () => {
ignore = true;
controller.abort();
};
}, [url]);
Guidance#
fetch처럼 취소 가능한 작업은AbortController를 우선 사용한다.- promise chain, JSON parsing 이후 처리, library callback처럼 취소가 완전히 전파되지 않을 수 있는 구간에는
ignoreflag 또는 request id 비교를 둔다. - subscription, timer, event listener는 cleanup에서 반드시 unsubscribe/clear/remove 한다.
- warning을 suppress하는 방식은 해결책이 아니다. React 18에서는 warning이 없어도 stale async result와 race condition은 여전히 correctness 문제다.
- data fetching이 복잡해지면 framework loader 또는 TanStack Query/SWR 같은 cache/query layer를 고려한다.
Failure Modes#
useEffect(() => { fetch(...).then(setState) }, [])처럼 cleanup 없이 state를 갱신한다.- dependency 변경으로 여러 요청이 겹치는데 마지막으로 시작한 요청이 아니라 마지막으로 끝난 요청을 UI에 반영한다.
isMountedref를 전역 escape hatch처럼 남용해 실제 구독 해제나 request cancellation을 하지 않는다.- React 18에서 경고가 사라졌다는 이유로 cleanup을 생략한다.
- unrelated source 목록을 유지해 capsule의 신뢰도를 떨어뜨린다.
Sources#
- React Docs —
useEffect, fetching data with Effects: https://react.dev/reference/react/useEffect#fetching-data-with-effects - React 18 working group discussion — removal of unmounted state update warning: https://github.com/reactwg/react-18/discussions/82
- MDN —
AbortController: https://developer.mozilla.org/en-US/docs/Web/API/AbortController - StackOverflow — historical warning context: https://stackoverflow.com/questions/53949393/cant-perform-a-react-state-update-on-an-unmounted-component
Supersedes / Cleanup Notes#
- Replace the previous source list because it included unrelated FastAPI, Kubernetes, Docker, and PostgreSQL links from external mining noise.
- Preserve the core insight: async effects need an explicit teardown/race-guard path.
Sagwan Revalidation 2026-06-14T21:03:50Z#
- verdict:
ok - note: React 18+ 동작과 AbortController/ignore cleanup 권장은 여전히 유효함