HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🐣
프론트엔드 데브코스 4기 교육생
/
😃
이동근팀
/
react@18.2.0
react@18.2.0
/
useTransition

useTransition

useTransition은 UI를 차단하지 않고 state를 업데이트할 수 있는 React 훅입니다.
const [isPending, startTransition] = useTransition()
  • 참조
    • useTransition()
    • startTransition function
  • 사용법
    • 업데이트를 논블로킹 트랜지션으로 표시하기
    • 트랜지션에서 상위 컴포넌트 업데이트하기
    • 트랜지션 중에 ‘보류중’ state 표시하기
    • 원치 않는 로딩 표시 방지하기
    • Suspense가 도입된 라우터 구축하기
  • 문제 해결
    • 트랜지션에서 input 업데이트가 작동하지 않습니다
    • React가 state 업데이트를 트랜지션으로 처리하지 않습니다.
    • 컴포넌트 외부에서 useTransition을 호출하고 싶습니다
    • startTransition에 전달한 함수는 즉시 실행됩니다

참조

useTransition()

컴포넌트의 최상위 레벨에서 useTransition을 호출하여 일부 state 업데이트를 트랜지션으로 표시합니다.
import { useTransition } from 'react'; function TabContainer() { const [isPending, startTransition] = useTransition(); // ... }
아래에서 더 많은 예시를 확인하세요.

Parameters매개변수

useTransition은 매개변수를 받지 않습니다.

Returns반환값

useTransition은 정확히 두 개의 항목이 있는 배열을 반환합니다:
  1. 보류 중인 트랜지션이 있는지 여부를 알려주는 isPending 플래그
  1. state 업데이트를 트랜지션으로 표시할 수 있는 startTransition 함수

startTransition function

useTransition이 반환하는 startTransition 함수를 사용하면 state 업데이트를 트랜지션으로 표시할 수 있습니다.
function TabContainer() { const [isPending, startTransition] = useTransition(); const [tab, setTab] = useState('about'); function selectTab(nextTab) { startTransition(() => { setTab(nextTab); }); } // ... }

매개변수

  • scope: 하나 이상의 set 함수를 호출하여 일부 state를 업데이트하는 함수. React는 매개변수 없이 scope를 즉시 호출하고 scope 함수 호출 중에 동기적으로 예약된 모든 state 업데이트를 트랜지션으로 표시합니다. 이는 논블로킹이고, 원치 않는 로딩을 표시하지 않을 것입니다.

반환값

startTransition은 아무것도 반환하지 않습니다.

주의사항

  • useTransition은 훅이므로 컴포넌트나 커스텀 훅 내부에서만 호출할 수 있습니다. 다른 곳(예: 데이터 라이브러리)에서 트랜지션을 시작해야 하는 경우, 대신 독립형 startTransition을 호출하세요.
  • 해당 state의 set 함수에 접근할 수 있는 경우에만 업데이트를 트랜지션으로 감쌀 수 있습니다. 일부 prop이나 커스텀 훅 값에 대한 응답으로 트랜지션을 시작하려면, 대신 useDeferredValue를 사용해보세요.
  • startTransition에 전달하는 함수는 동기식이어야 합니다. React는 이 함수를 즉시 실행하여, 실행하는 동안 발생하는 모든 state 업데이트를 트랜지션으로 표시합니다. 나중에 더 많은 state 업데이트를 수행하려고 하면(예: 타임아웃), 트랜지션으로 표시되지 않습니다.
  • 트랜지션으로 표시된 state 업데이트는 다른 state 업데이트에 의해 중단됩니다. 예를 들어, 트랜지션 내에서 차트 컴포넌트를 업데이트한 다음, 차트가 다시 렌더링되는 도중에 입력을 시작하면 React는 입력 업데이트를 처리한 후 차트 컴포넌트에서 렌더링 작업을 다시 시작합니다.
  • 트랜지션 업데이트는 텍스트 입력을 제어하는 데 사용할 수 없습니다.
  • 진행 중인 트랜지션이 여러 개 있는 경우, React는 현재 트랜지션을 함께 일괄 처리합니다. 이는 향후 릴리스에서 제거될 가능성이 높은 제한 사항입니다.

사용법

업데이트를 논블로킹 트랜지션으로 표시하기

컴포넌트의 최상위 레벨에서 useTransition을 호출하여 state 업데이트를 논블로킹 트랜지션으로 표시하세요.
import { useState, useTransition } from 'react'; function TabContainer() { const [isPending, startTransition] = useTransition(); // ... }
useTransition은 정확히 두 개의 항목이 있는 배열을 반환합니다:
  1. 보류 중인 트랜지션 이 있는지 여부를 알려주는 isPending 플래그를 선택합니다.
  1. state 업데이트를 트랜지션으로 표시할 수 있는 startTransition 함수입니다.
그런 다음, 다음과 같이 state 업데이트를 트랜지션으로 표시할 수 있습니다:
function TabContainer() { const [isPending, startTransition] = useTransition(); const [tab, setTab] = useState('about'); function selectTab(nextTab) { startTransition(() => { setTab(nextTab); }); } // ... }
트랜지션을 사용하면 느린 디바이스에서도 사용자 인터페이스 업데이트의 반응성을 유지할 수 있습니다.
트랜지션을 사용하면 리렌더링 도중에도 UI가 반응성을 유지합니다. 예를 들어, 사용자가 탭을 클릭했다가 마음이 바뀌어 다른 탭을 클릭하면 첫 번째 리렌더링이 완료될 때까지 기다릴 필요 없이 다른 탭을 클릭할 수 있습니다.

useTransition과 일반 state 업데이트의 차이점

1. 트랜지션에서 현재 탭 업데이트하기
2. 트랜지션 없이 현재 탭 업데이트하기
예시

트랜지션에서 상위 컴포넌트 업데이트하기

useTransition 호출에서도 부모 컴포넌트의 state를 업데이트할 수 있습니다. 예를 들어, 이 TabButton 컴포넌트는 onClick 로직을 트랜지션으로 감쌉니다:
export default function TabButton({ children, isActive, onClick }) { const [isPending, startTransition] = useTransition(); if (isActive) { return <b>{children}</b> } return ( <button onClick={() => { startTransition(() => { onClick(); }); }}> {children} </button> ); }
부모 컴포넌트가 onClick 이벤트 핸들러 내에서 state를 업데이트하기 때문에 해당 state 업데이트는 트랜지션으로 표시됩니다. 그렇기 때문에 앞의 예시처럼 ‘Posts’을 클릭한 다음 바로 ‘Contact’를 클릭할 수 있습니다. 선택한 탭을 업데이트하는 것은 트랜지션으로 표시되므로 사용자 상호작용을 차단하지 않습니다.

트랜지션 중에 ‘보류중’ state 표시하기

useTransition이 반환하는 isPending boolean 값을 사용하여 트랜지션이 진행 중임을 사용자에게 표시할 수 있습니다. 예를 들어, 탭 버튼은 특별한 ‘pending’ state를 가질 수 있습니다:
function TabButton({ children, isActive, onClick }) { const [isPending, startTransition] = useTransition(); // ... if (isPending) { return <b className="pending">{children}</b>; } // ...
이제 탭 버튼 자체가 바로 업데이트되므로 ‘Posts’를 클릭하는 반응이 더 빨라진 것을 확인할 수 있습니다:

원치 않는 로딩 표시 방지하기

이 예제에서 PostsTab 컴포넌트는 Suspense가 도입된 데이터 소스를 사용하여 일부 데이터를 가져옵니다. “Posts” 탭을 클릭하면 PostsTab 컴포넌트가 중단되어 가장 가까운 로딩 폴백이 나타납니다
 
로딩 표시를 위해 전체 탭 컨테이너를 숨기면 UX가 어색해집니다. TabButton에 useTransition을 추가하면 대신 탭 버튼에 ‘보류중’ state를 표시할 수 있습니다.
‘Posts’를 클릭하면 더 이상 전체 탭 컨테이너가 스피너로 바뀌지 않습니다:
Suspense와 함께 트랜지션을 사용하는 방법에 대해 자세히 알아보세요.

Note

트랜지션은 탭 컨테이너와 같이 이미 노출된 콘텐츠를 숨기지 않을 수 있을 만큼만 “대기”합니다. Posts 탭에 중첩된 <Suspense> 바운더리가 있는 경우 트랜지션은 이를 “대기”하지 않습니다.

Suspense가 도입된 라우터 구축하기

React 프레임워크나 라우터를 구축하는 경우 페이지 네비게이션을 트랜지션으로 표시하는 것이 좋습니다.
function Router() { const [page, setPage] = useState('/'); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } // ...
두 가지 이유로 이 방법을 권장합니다:
  • 트랜지션은 중단 가능하므로, 사용자는 다시 렌더링이 완료될 때까지 기다리지 않고 바로 클릭할 수 있습니다.
  • 트랜지션은 원치 않는 로딩 표시를 방지하여, 사용자가 네비게이션 시 갑작스럽게 이동 하는 것을 방지할 수 있습니다.

Note

Suspense가 도입된 라우터는 기본적으로 네비게이션 업데이트를 트랜지션으로 감싸고 있을 것이라고 간주합니다.

문제 해결

트랜지션에서 input 업데이트가 작동하지 않습니다

input을 제어하는 state 변수에는 트랜지션을 사용할 수 없습니다:
const [text, setText] = useState(''); // ... function handleChange(e) { // ❌ Can't use transitions for controlled input state startTransition(() => { setText(e.target.value); }); } // ... return <input value={text} onChange={handleChange} />;
이는 트랜지션은 논블로킹이지만 변경 이벤트에 대한 응답으로 input을 업데이트하는 것은 동기적으로 이루어져야 하기 때문입니다. input에 대한 응답으로 트랜지션을 실행하려면 두 가지 옵션이 있습니다:
  1. input의 (항상 동기적으로 업데이트되는) state와 트랜지션 실행시 업데이트할 state 변수를 각각 선언할 수 있습니다. 이를 통해 동기 state를 사용하여 input을 제어하고, (input보다 “지연”되는) 트랜지션 state 변수를 나머지 렌더링 로직에 전달할 수 있습니다.
  1. 또는 하나의 state 변수를 가지고, 실제 값보다 “지연”되는 useDeferredValue를 추가할 수 있습니다. 그러면 새로운 값을 자동으로 “따라잡기” 위해 논블로킹 리렌더를 촉발합니다.

React가 state 업데이트를 트랜지션으로 처리하지 않습니다.

state 업데이트를 트랜지션으로 감쌀 때는 startTransition 호출 중 state 업데이트가 발생해야 합니다:
startTransition(() => { // ✅ Setting state *during* startTransition call setPage('/about'); });
startTransition에 전달하는 함수는 동기식이어야 합니다.
이와 같은 업데이트는 트랜지션으로 표시할 수 없습니다:
startTransition(() => { // ❌ Setting state *after* startTransition call setTimeout(() => { setPage('/about'); }, 1000); });
대신 이렇게 할 수 있습니다:
setTimeout(() => { startTransition(() => { // ✅ Setting state *during* startTransition call setPage('/about'); }); }, 1000);
마찬가지로 업데이트를 이와 같은 트랜지션으로 표시할 수 없습니다:
startTransition(async () => { await someAsyncFunction(); // ❌ Setting state *after* startTransition call setPage('/about'); });
대신 이 방법은 작동합니다:
await someAsyncFunction(); startTransition(() => { // ✅ Setting state *during* startTransition call setPage('/about'); });

컴포넌트 외부에서 useTransition을 호출하고 싶습니다

훅이기 때문에 컴포넌트 외부에서 useTransition을 호출할 수 없습니다. 이 경우 대신 독립형 startTransition 메서드를 사용하세요. 동일한 방식으로 작동하지만 isPending 표시기를 제공하지 않습니다.

startTransition에 전달한 함수는 즉시 실행됩니다

이 코드를 실행하면 1, 2, 3이 인쇄됩니다:
console.log(1); startTransition(() => { console.log(2); setPage('/about'); }); console.log(3);
1, 2, 3을 출력할 것으로 예상됩니다. startTransition에 전달한 함수는 지연되지 않습니다. 브라우저의 setTimeout과 달리 나중에 콜백을 실행하지 않습니다. React는 함수를 즉시 실행하지만, 함수가 실행되는 동안 예약된 모든 state 업데이트는 트랜지션으로 표시됩니다. 이렇게 작동한다고 상상하면 됩니다:
// A simplified version of how React works let isInsideTransition = false; function startTransition(scope) { isInsideTransition = true; scope(); isInsideTransition = false; } function setState() { if (isInsideTransition) { // ... schedule a transition state update ... } else { // ... schedule an urgent state update ... } }