///

React useState Async & Closure Trap Capsule

useState의 setter는 class setState처럼 비동기이며, 호출 직후 state 변수는 여전히 이전 값이다. 더 근본 문제는 클로저 : 렌더링마다 새 클로저가 만들어지고 setTimeout/setInterval/외부 콜백은 자신이 만들어진 시점의 state를 계속 본다.

///

kind: capsule status: active visibility: private license: CC-BY-SA-4.0 summary: useState setter는 비동기이고 다음 렌더링에 반영. 이전 state 기반 업데이트는 함수형 setter(prev => next) 사용. setTimeout 내부는 stale closure 주의. tags: - react - hooks - usestate - closure - capsule


React useState Async & Closure Trap Capsule

Summary#

useState의 setter는 class setState처럼 비동기이며, 호출 직후 state 변수는 여전히 이전 값이다. 더 근본 문제는 클로저: 렌더링마다 새 클로저가 만들어지고 setTimeout/setInterval/외부 콜백은 자신이 만들어진 시점의 state를 계속 본다.

Claim#

문제 재현#

function Counter() {
  const [n, setN] = useState(0);
  const inc = () => {
    setN(n + 1);
    setN(n + 1);  // 여전히 0+1 — 반영 1회만
    console.log(n);  // 여전히 0
  };
  return <button onClick={inc}>{n}</button>;
}

해법 1: 함수형 업데이트#

setN(prev => prev + 1);
setN(prev => prev + 1);  // 이제 +2

Reducer 큐로 들어가 순서대로 적용.

해법 2: 새 값을 변수로 저장#

const next = n + 1;
setN(next);
doSomething(next);  // 최신값 사용

해법 3: useEffect로 반영 시점 캡처#

useEffect(() => {
  console.log('n changed to', n);
}, [n]);

Stale closure 트랩#

useEffect(() => {
  const id = setInterval(() => {
    setN(n + 1);       // n은 effect 생성 시점 값에 고정 — 영원히 0 → 1 반복
  }, 1000);
  return () => clearInterval(id);
}, []);  // 빈 deps라 한 번만 설정

→ 함수형 setter로 우회:

setN(prev => prev + 1);

Scope#

  • React 16.8+ (Hooks). React 18 automatic batching도 동일 규칙
  • React Compiler (RC)가 일부 wrap해주지만 여전히 setter 비동기성은 유지

Caveats#

  • flushSync로 동기 flush 가능하지만 성능 저하 — 예외적으로만 사용
  • 여러 setter를 동일 이벤트에서 호출하면 React 18은 자동 batch → 한 번만 렌더링
  • useRef는 mutable box — 렌더링과 무관한 값 추적에 유용하지만 UI 갱신 트리거 안 함

Source#

  • Q: https://stackoverflow.com/q/54069253
  • A: https://stackoverflow.com/a/54069332 — by Shubham Khatri
  • License: CC BY-SA 4.0 (Stack Exchange)
  • last_edit: 2024
  • 조회일: 2026-04-19

Sagwan Revalidation 2026-04-19T00:40:21Z#

  • verdict: ok
  • note: React 18 자동 배칭·함수형 setter·stale closure 규칙 모두 2026년 현재 practice와 일치하며 수정 불필요.