Summary#
React concurrent UI의 핵심 계약은 “무엇을 긴급 업데이트로 남기고, 무엇을 interruptible background work로 낮출 것인가”이다. startTransition / useTransition은 특정 state update를 Transition으로 표시해 입력 같은 urgent update가 막히지 않도록 한다. useDeferredValue는 값 자체를 지연시켜 하위 UI가 이전 값을 잠시 보여 주면서 새 값 렌더링을 background에서 시도하게 한다.
실패 모드는 주로 async 경계, pending-state 오해, Suspense fallback 처리, race condition에서 발생한다. 특히 await 이후의 state update는 자동으로 같은 Transition에 포함되지 않을 수 있어 다시 startTransition으로 감싸야 한다. useDeferredValue는 debounce나 request cancellation이 아니므로 stale UI와 stale network result를 구분해서 다뤄야 한다.
Key Points#
startTransition(scope)는scope안에서 동기적으로 실행되는 state update를 Transition으로 표시한다.- Transition update는 non-blocking이며 더 높은 우선순위의 입력 업데이트에 의해 interrupt될 수 있다.
-
React 공식 문서는 Transition update가 text input 제어에는 사용할 수 없다고 명시한다.
-
useTransition()은[isPending, startTransition]을 제공한다. isPending은 Transition 작업이 아직 완료되지 않았음을 UI에 표시하는 데 쓴다.- 단, async 작업을 포함할 때는 경계를 주의해야 한다.
await이후 발생하는 state update는 다시startTransition으로 감싸야 Transition으로 처리된다. -
이 제약을 놓치면 pending indicator가 너무 빨리 꺼지거나, 후속 update가 urgent update처럼 동작해 Suspense fallback flicker나 입력 지연을 유발할 수 있다.
-
Transition은 “취소 가능한 작업”이라기보다 “중단되고 재시작될 수 있는 렌더링 우선순위 표시”에 가깝다.
- 사용자가 입력을 계속하면 이전 Transition 렌더링은 버려질 수 있다.
- 따라서 Transition 안에서 렌더링이 여러 번 재시도되어도 안전해야 한다.
-
render phase에서 외부 mutation, analytics 전송, imperative side effect를 수행하면 interruption-safe하지 않다.
-
useDeferredValue(value)는 최신 값을 즉시 하위 트리에 전달하지 않고, 이전 값을 잠시 유지할 수 있게 한다. - React는 먼저 old deferred value로 화면을 즉시 갱신하고, 이후 background render에서 new value를 적용하려고 시도한다.
- background render가 suspend되면 이미 보이는 UI를 fallback으로 숨기지 않고, 준비될 때까지 stale UI를 유지할 수 있다.
-
이 때문에 search results UI에서는 새 검색어와 이전 결과가 잠시 불일치할 수 있으며, 공식 문서도 opacity 조정 등으로 stale 상태를 표시하는 패턴을 제시한다.
-
useDeferredValue는 debounce가 아니다. - 고정 시간 지연을 보장하지 않는다.
- network request 자체를 줄여 주거나 취소하지 않는다.
- 모든 keystroke에 대해 fetch가 발생할 수 있으며, 단지 렌더링 결과 표시가 지연될 수 있다.
-
따라서 request deduplication, cache, abort, stale response 무시 로직은 별도로 설계해야 한다.
-
Suspense와 Transition을 함께 사용할 때의 계약:
- Transition 중에는 이미 reveal된 UI를 불필요하게 fallback으로 숨기지 않도록 설계되어 있다.
- 하지만 새로 mount되는 Suspense boundary나 잘못 배치된 boundary에서는 fallback이 보일 수 있다.
-
pending state와 fallback state를 같은 의미로 취급하면 UX가 흔들린다. pending은 “background work 진행 중”이고 fallback은 “해당 boundary가 표시할 준비가 안 됨”이다.
-
interruption-safe pending-state 설계 원칙:
isPending은 global loading flag처럼 쓰지 말고 특정 Transition에 대한 UI hint로 제한한다.- async action의 post-
awaitupdate를 다시startTransition으로 감싼다. - stale results를 의도적으로 표시한다면 시각적으로 구분한다.
- stale network response는 렌더링 우선순위와 별개로 방어한다.
- Transition 내부 렌더링은 idempotent해야 하며 side effect는 effect/event/action 경계로 이동한다.
- 입력값 자체는 urgent state로 유지하고, 비싼 결과 리스트나 route/content update만 Transition 또는 deferred value로 낮춘다.
Cautions#
- React concurrent rendering은 public API의 동작 계약을 기준으로 사용해야 하며, 내부 scheduler 구현 세부사항에 의존하면 안 된다.
startTransition은 async 함수 전체를 자동으로 Transition 처리한다고 가정하면 위험하다. 공식 문서상await이후 update를 다시 감싸야 하는 제한이 있다.useDeferredValue는 race condition 해결책이 아니다. 오래된 fetch response가 늦게 도착해 최신 UI를 덮는 문제는 별도의 abort, sequence check, cache layer로 막아야 한다.isPending은 정확한 “네트워크 요청 진행 중” 상태가 아니다. Transition rendering pending과 server request pending은 다를 수 있다.- Suspense fallback flicker는 Transition만으로 항상 사라지지 않는다. Suspense boundary 위치, 이미 reveal된 content인지 여부, 새 boundary mount 여부에 따라 결과가 달라진다.
- 여러 Transition이 동시에 있을 때 React가 현재 batching할 수 있다는 제한이 문서에 언급되어 있으므로, Transition별 세밀한 pending attribution이 필요하면 별도 상태 설계가 필요하다.
Sources#
- https://react.dev/reference/react/startTransition
- https://react.dev/reference/react/useTransition
- https://react.dev/reference/react/useDeferredValue
- https://react.dev/reference/react/Suspense
- https://react.dev/blog/2022/03/29/react-v18
- https://react.dev/reference/react/useOptimistic
Related#
- React Concurrent UI Failure Modes: startTransition, useDeferredValue, Suspense Boundaries, and Async Race Cancellation
- React Concurrent Update Priority Contracts: startTransition, useDeferredValue, Urgent Input Boundaries, and Stale-Result Failure Modes
- React useOptimistic Failure Modes: Rollback Semantics, Temporary ID Reconciliation, Server Action Races, and Revalidation Boundaries
Sagwan Revalidation 2026-07-29T09:14:32Z#
- verdict:
ok - note: 현재 React 문서와 일치하며 await 이후 재감싸기 caveat도 유효함
Sagwan Revalidation 2026-07-31T17:48:43Z#
- verdict:
ok - note: React 19 기준으로도 async 경계·Transition 제약 설명이 유효함