useTransition
is a React Hook that lets you update the state without blocking the UI.
useTransition
은 UI를 차단하지 않고 state를 업데이트할 수 있는 React 훅입니다.
const [isPending, startTransition] = useTransition()
- Reference참조
- Usage사용법
- Marking a state update as a non-blocking Transitionstate 업데이트를 논블로킹 트랜지션으로 표시하기
- Updating the parent component in a Transition트랜지션에서 상위 컴포넌트 업데이트하기
- Displaying a pending visual state during the Transition트랜지션 중에 ‘보류중’ state 표시하기
- Preventing unwanted loading indicators원치 않는 로딩 표시 방지하기
- Building a Suspense-enabled routerSuspense가 도입된 라우터 구축하기
- Displaying an error to users with an error boundary 에러 바운더리로 사용자에게 오류 표시하기
- Troubleshooting문제 해결
- Updating an input in a Transition doesn’t work트랜지션에서 input 업데이트가 작동하지 않습니다
- React doesn’t treat my state update as a TransitionReact가 state 업데이트를 트랜지션으로 처리하지 않습니다.
- I want to call
useTransition
from outside a component컴포넌트 외부에서useTransition
을 호출하고 싶습니다 - The function I pass to
startTransition
executes immediatelystartTransition
에 전달한 함수는 즉시 실행됩니다
Reference참조
useTransition()
Call useTransition
at the top level of your component to mark some state updates as Transitions.
컴포넌트의 최상위 레벨에서 useTransition
을 호출하여 일부 state 업데이트를 트랜지션으로 표시합니다.
import { useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}
See more examples below. 아래에서 더 많은 예시를 확인하세요.
Parameters매개변수
useTransition
does not take any parameters.
useTransition
은 매개변수를 받지 않습니다.
Returns반환값
useTransition
returns an array with exactly two items:
useTransition
은 정확히 두 개의 항목이 있는 배열을 반환합니다:
-
The
isPending
flag that tells you whether there is a pending Transition. 보류 중인 트랜지션이 있는지 여부를 알려주는isPending
플래그 -
The
startTransition
function that lets you mark a state update as a Transition. state 업데이트를 트랜지션으로 표시할 수 있는startTransition
함수
startTransition
function
The startTransition
function returned by useTransition
lets you mark a state update as a Transition.
useTransition
이 반환하는 startTransition
함수를 사용하면 state 업데이트를 트랜지션으로 표시할 수 있습니다.
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}
Parameters매개변수
scope
: A function that updates some state by calling one or moreset
functions. React immediately callsscope
with no parameters and marks all state updates scheduled synchronously during thescope
function call as Transitions. They will be non-blocking and will not display unwanted loading indicators.scope
: 하나 이상의set
함수를 호출하여 일부 state를 업데이트하는 함수. React는 매개변수 없이scope
를 즉시 호출하고scope
함수 호출 중에 동기적으로 예약된 모든 state 업데이트를 트랜지션으로 표시합니다. 이는 논블로킹이고, 원치 않는 로딩을 표시하지 않을 것입니다.
Returns반환값
startTransition
does not return anything.
startTransition
은 아무것도 반환하지 않습니다.
Caveats주의사항
-
useTransition
is a Hook, so it can only be called inside components or custom Hooks. If you need to start a Transition somewhere else (for example, from a data library), call the standalonestartTransition
instead.useTransition
은 훅이므로 컴포넌트나 커스텀 훅 내부에서만 호출할 수 있습니다. 다른 곳(예: 데이터 라이브러리)에서 트랜지션을 시작해야 하는 경우, 대신 독립형startTransition
을 호출하세요. -
You can wrap an update into a Transition only if you have access to the
set
function of that state. If you want to start a Transition in response to some prop or a custom Hook value, tryuseDeferredValue
instead. 해당 state의set
함수에 접근할 수 있는 경우에만 업데이트를 트랜지션으로 감쌀 수 있습니다. 일부 prop이나 커스텀 훅 값에 대한 응답으로 트랜지션을 시작하려면, 대신useDeferredValue
를 사용해보세요. -
The function you pass to
startTransition
must be synchronous. React immediately executes this function, marking all state updates that happen while it executes as Transitions. If you try to perform more state updates later (for example, in a timeout), they won’t be marked as Transitions.startTransition
에 전달하는 함수는 동기식이어야 합니다. React는 이 함수를 즉시 실행하여, 실행하는 동안 발생하는 모든 state 업데이트를 트랜지션으로 표시합니다. 나중에 더 많은 state 업데이트를 수행하려고 하면(예: 타임아웃), 트랜지션으로 표시되지 않습니다. -
A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input update. 트랜지션으로 표시된 state 업데이트는 다른 state 업데이트에 의해 중단됩니다. 예를 들어, 트랜지션 내에서 차트 컴포넌트를 업데이트한 다음, 차트가 다시 렌더링되는 도중에 입력을 시작하면 React는 입력 업데이트를 처리한 후 차트 컴포넌트에서 렌더링 작업을 다시 시작합니다.
-
Transition updates can’t be used to control text inputs. 트랜지션 업데이트는 텍스트 입력을 제어하는 데 사용할 수 없습니다.
-
If there are multiple ongoing Transitions, React currently batches them together. This is a limitation that will likely be removed in a future release. 진행 중인 트랜지션이 여러 개 있는 경우, React는 현재 트랜지션을 함께 일괄 처리합니다. 이는 향후 릴리스에서 제거될 가능성이 높은 제한 사항입니다.
Usage사용법
Marking a state update as a non-blocking Transitionstate 업데이트를 논블로킹 트랜지션으로 표시하기
Call useTransition
at the top level of your component to mark state updates as non-blocking Transitions.
컴포넌트의 최상위 레벨에서 useTransition
을 호출하여 state 업데이트를 논블로킹 트랜지션으로 표시하세요.
import { useState, useTransition } from 'react';
function TabContainer() {
const [isPending, startTransition] = useTransition();
// ...
}
useTransition
returns an array with exactly two items:
useTransition
은 정확히 두 개의 항목이 있는 배열을 반환합니다:
-
The
isPending
flag that tells you whether there is a pending Transition. 보류 중인 트랜지션 이 있는지 여부를 알려주는isPending
플래그를 선택합니다. -
The
startTransition
function that lets you mark a state update as a Transition. state 업데이트를 트랜지션으로 표시할 수 있는startTransition
함수입니다.
You can then mark a state update as a Transition like this: 그런 다음, 다음과 같이 state 업데이트를 트랜지션으로 표시할 수 있습니다:
function TabContainer() {
const [isPending, startTransition] = useTransition();
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}
Transitions let you keep the user interface updates responsive even on slow devices. 트랜지션을 사용하면 느린 디바이스에서도 사용자 인터페이스 업데이트의 반응성을 유지할 수 있습니다.
With a Transition, your UI stays responsive in the middle of a re-render. For example, if the user clicks a tab but then change their mind and click another tab, they can do that without waiting for the first re-render to finish. 트랜지션을 사용하면 리렌더링 도중에도 UI가 반응성을 유지합니다. 예를 들어, 사용자가 탭을 클릭했다가 마음이 바뀌어 다른 탭을 클릭하면 첫 번째 리렌더링이 완료될 때까지 기다릴 필요 없이 다른 탭을 클릭할 수 있습니다.
Example 1 of 2: Updating the current tab in a Transition트랜지션에서 현재 탭 업데이트하기
In this example, the “Posts” tab is artificially slowed down so that it takes at least a second to render. 이 예제에서는 “Posts” 탭이 인위적으로 느려지도록 하여 렌더링하는 데 최소 1초가 걸리도록 했습니다.
Click “Posts” and then immediately click “Contact”. Notice that this interrupts the slow render of “Posts”. The “Contact” tab shows immediately. Because this state update is marked as a Transition, a slow re-render did not freeze the user interface. “Posts”를 클릭한 다음 바로 “Contact”를 클릭합니다. 이렇게 하면 “Posts”의 느린 렌더링이 중단됩니다. “Contact” 탭이 즉시 표시됩니다. 이 state 업데이트는 트랜지션으로 표시되므로 느리게 다시 렌더링해도 사용자 인터페이스가 멈추지 않습니다.
import { useState, useTransition } from 'react'; import TabButton from './TabButton.js'; import AboutTab from './AboutTab.js'; import PostsTab from './PostsTab.js'; import ContactTab from './ContactTab.js'; export default function TabContainer() { const [isPending, startTransition] = useTransition(); const [tab, setTab] = useState('about'); function selectTab(nextTab) { startTransition(() => { setTab(nextTab); }); } return ( <> <TabButton isActive={tab === 'about'} onClick={() => selectTab('about')} > About </TabButton> <TabButton isActive={tab === 'posts'} onClick={() => selectTab('posts')} > Posts (slow) </TabButton> <TabButton isActive={tab === 'contact'} onClick={() => selectTab('contact')} > Contact </TabButton> <hr /> {tab === 'about' && <AboutTab />} {tab === 'posts' && <PostsTab />} {tab === 'contact' && <ContactTab />} </> ); }
Updating the parent component in a Transition트랜지션에서 상위 컴포넌트 업데이트하기
You can update a parent component’s state from the useTransition
call, too. For example, this TabButton
component wraps its onClick
logic in a Transition:
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>
);
}
Because the parent component updates its state inside the onClick
event handler, that state update gets marked as a Transition. This is why, like in the earlier example, you can click on “Posts” and then immediately click “Contact”. Updating the selected tab is marked as a Transition, so it does not block user interactions.
부모 컴포넌트가 onClick
이벤트 핸들러 내에서 state를 업데이트하기 때문에 해당 state 업데이트는 트랜지션으로 표시됩니다. 그렇기 때문에 앞의 예시처럼 ‘Posts’을 클릭한 다음 바로 ‘Contact’를 클릭할 수 있습니다. 선택한 탭을 업데이트하는 것은 트랜지션으로 표시되므로 사용자 상호작용을 차단하지 않습니다.
import { useTransition } from 'react'; export default function TabButton({ children, isActive, onClick }) { const [isPending, startTransition] = useTransition(); if (isActive) { return <b>{children}</b> } return ( <button onClick={() => { startTransition(() => { onClick(); }); }}> {children} </button> ); }
Displaying a pending visual state during the Transition트랜지션 중에 ‘보류중’ state 표시하기
You can use the isPending
boolean value returned by useTransition
to indicate to the user that a Transition is in progress. For example, the tab button can have a special “pending” visual state:
useTransition
이 반환하는 isPending
boolean 값을 사용하여 트랜지션이 진행 중임을 사용자에게 표시할 수 있습니다. 예를 들어, 탭 버튼은 특별한 ‘pending’ state를 가질 수 있습니다:
function TabButton({ children, isActive, onClick }) {
const [isPending, startTransition] = useTransition();
// ...
if (isPending) {
return <b className="pending">{children}</b>;
}
// ...
Notice how clicking “Posts” now feels more responsive because the tab button itself updates right away: 이제 탭 버튼 자체가 바로 업데이트되므로 ‘Posts’를 클릭하는 반응이 더 빨라진 것을 확인할 수 있습니다:
import { useTransition } from 'react'; export default function TabButton({ children, isActive, onClick }) { const [isPending, startTransition] = useTransition(); if (isActive) { return <b>{children}</b> } if (isPending) { return <b className="pending">{children}</b>; } return ( <button onClick={() => { startTransition(() => { onClick(); }); }}> {children} </button> ); }
Preventing unwanted loading indicators원치 않는 로딩 표시 방지하기
In this example, the PostsTab
component fetches some data using a Suspense-enabled data source. When you click the “Posts” tab, the PostsTab
component suspends, causing the closest loading fallback to appear:
이 예제에서 PostsTab
컴포넌트는 Suspense가 도입된 데이터 소스를 사용하여 일부 데이터를 가져옵니다. “Posts” 탭을 클릭하면 PostsTab
컴포넌트가 중단되어 가장 가까운 로딩 폴백이 나타납니다:
import { Suspense, useState } from 'react'; import TabButton from './TabButton.js'; import AboutTab from './AboutTab.js'; import PostsTab from './PostsTab.js'; import ContactTab from './ContactTab.js'; export default function TabContainer() { const [tab, setTab] = useState('about'); return ( <Suspense fallback={<h1>🌀 Loading...</h1>}> <TabButton isActive={tab === 'about'} onClick={() => setTab('about')} > About </TabButton> <TabButton isActive={tab === 'posts'} onClick={() => setTab('posts')} > Posts </TabButton> <TabButton isActive={tab === 'contact'} onClick={() => setTab('contact')} > Contact </TabButton> <hr /> {tab === 'about' && <AboutTab />} {tab === 'posts' && <PostsTab />} {tab === 'contact' && <ContactTab />} </Suspense> ); }
Hiding the entire tab container to show a loading indicator leads to a jarring user experience. If you add useTransition
to TabButton
, you can instead indicate display the pending state in the tab button instead.
로딩 표시를 위해 전체 탭 컨테이너를 숨기면 UX가 어색해집니다. TabButton
에 useTransition
을 추가하면 대신 탭 버튼에 ‘보류중’ state를 표시할 수 있습니다.
Notice that clicking “Posts” no longer replaces the entire tab container with a spinner: ‘Posts’를 클릭하면 더 이상 전체 탭 컨테이너가 스피너로 바뀌지 않습니다:
import { useTransition } from 'react'; export default function TabButton({ children, isActive, onClick }) { const [isPending, startTransition] = useTransition(); if (isActive) { return <b>{children}</b> } if (isPending) { return <b className="pending">{children}</b>; } return ( <button onClick={() => { startTransition(() => { onClick(); }); }}> {children} </button> ); }
Read more about using Transitions with Suspense. Suspense와 함께 트랜지션을 사용하는 방법에 대해 자세히 알아보세요.
Building a Suspense-enabled routerSuspense가 도입된 라우터 구축하기
If you’re building a React framework or a router, we recommend marking page navigations as Transitions. React 프레임워크나 라우터를 구축하는 경우 페이지 네비게이션을 트랜지션으로 표시하는 것이 좋습니다.
function Router() {
const [page, setPage] = useState('/');
const [isPending, startTransition] = useTransition();
function navigate(url) {
startTransition(() => {
setPage(url);
});
}
// ...
This is recommended for two reasons: 두 가지 이유로 이 방법을 권장합니다:
-
Transitions are interruptible, which lets the user click away without waiting for the re-render to complete. 트랜지션은 중단 가능하므로, 사용자는 다시 렌더링이 완료될 때까지 기다리지 않고 바로 클릭할 수 있습니다.
-
Transitions prevent unwanted loading indicators, which lets the user avoid jarring jumps on navigation. 트랜지션은 원치 않는 로딩 표시를 방지하여, 사용자가 네비게이션 시 갑작스럽게 이동 하는 것을 방지할 수 있습니다.
Here is a tiny simplified router example using Transitions for navigations. 다음은 네비게이션을 위해 트랜지션을 사용하는 아주 간단한 라우터 예제입니다.
import { Suspense, useState, useTransition } from 'react'; import IndexPage from './IndexPage.js'; import ArtistPage from './ArtistPage.js'; import Layout from './Layout.js'; export default function App() { return ( <Suspense fallback={<BigSpinner />}> <Router /> </Suspense> ); } function Router() { const [page, setPage] = useState('/'); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } let content; if (page === '/') { content = ( <IndexPage navigate={navigate} /> ); } else if (page === '/the-beatles') { content = ( <ArtistPage artist={{ id: 'the-beatles', name: 'The Beatles', }} /> ); } return ( <Layout isPending={isPending}> {content} </Layout> ); } function BigSpinner() { return <h2>🌀 Loading...</h2>; }
Displaying an error to users with an error boundary 에러 바운더리로 사용자에게 오류 표시하기
If a function passed to startTransition
throws an error, you can display an error to your user with an error boundary. To use an error boundary, wrap the component where you are calling the useTransition
in an error boundary. Once the function passed to startTransition
errors, the fallback for the error boundary will be displayed.
startTransition
에 전달된 함수가 에러를 발생시키면, 에러 바운더리를 사용하여 사용자에게 에러를 표시할 수 있습니다. 에러 바운더리를 사용하려면 useTransition
을 호출하는 컴포넌트를 에러 바운더리로 감싸면 됩니다. startTransition
에 전달된 함수가 에러를 발생시키면 에러 바운더리에 대한 폴백이 표시됩니다.
import { useTransition } from "react"; import { ErrorBoundary } from "react-error-boundary"; export function AddCommentContainer() { return ( <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}> <AddCommentButton /> </ErrorBoundary> ); } function addComment(comment) { // For demonstration purposes to show Error Boundary if (comment == null) { throw new Error("Example Error: An error thrown to trigger error boundary"); } } function AddCommentButton() { const [pending, startTransition] = useTransition(); return ( <button disabled={pending} onClick={() => { startTransition(() => { // Intentionally not passing a comment // so error gets thrown addComment(); }); }} > Add comment </button> ); }
Troubleshooting문제 해결
Updating an input in a Transition doesn’t work트랜지션에서 input 업데이트가 작동하지 않습니다
You can’t use a Transition for a state variable that controls an 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} />;
This is because Transitions are non-blocking, but updating an input in response to the change event should happen synchronously. If you want to run a Transition in response to typing, you have two options: 이는 트랜지션은 논블로킹이지만 변경 이벤트에 대한 응답으로 input을 업데이트하는 것은 동기적으로 이루어져야 하기 때문입니다. input에 대한 응답으로 트랜지션을 실행하려면 두 가지 옵션이 있습니다:
-
You can declare two separate state variables: one for the input state (which always updates synchronously), and one that you will update in a Transition. This lets you control the input using the synchronous state, and pass the Transition state variable (which will “lag behind” the input) to the rest of your rendering logic. input의 (항상 동기적으로 업데이트되는) state와 트랜지션 실행시 업데이트할 state 변수를 각각 선언할 수 있습니다. 이를 통해 동기 state를 사용하여 input을 제어하고, (input보다 “지연”되는) 트랜지션 state 변수를 나머지 렌더링 로직에 전달할 수 있습니다.
-
Alternatively, you can have one state variable, and add
useDeferredValue
which will “lag behind” the real value. It will trigger non-blocking re-renders to “catch up” with the new value automatically. 또는 하나의 state 변수를 가지고, 실제 값보다 “지연”되는useDeferredValue
를 추가할 수 있습니다. 그러면 새로운 값을 자동으로 “따라잡기” 위해 논블로킹 리렌더를 촉발합니다.
React doesn’t treat my state update as a TransitionReact가 state 업데이트를 트랜지션으로 처리하지 않습니다.
When you wrap a state update in a Transition, make sure that it happens during the startTransition
call:
state 업데이트를 트랜지션으로 감쌀 때는 startTransition
호출 중 state 업데이트가 발생해야 합니다:
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});
The function you pass to startTransition
must be synchronous.
startTransition
에 전달하는 함수는 동기식이어야 합니다.
You can’t mark an update as a Transition like this: 이와 같은 업데이트는 트랜지션으로 표시할 수 없습니다:
startTransition(() => {
// ❌ Setting state *after* startTransition call
setTimeout(() => {
setPage('/about');
}, 1000);
});
Instead, you could do this: 대신 이렇게 할 수 있습니다:
setTimeout(() => {
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});
}, 1000);
Similarly, you can’t mark an update as a Ttransition like this: 마찬가지로 업데이트를 이와 같은 트랜지션으로 표시할 수 없습니다:
startTransition(async () => {
await someAsyncFunction();
// ❌ Setting state *after* startTransition call
setPage('/about');
});
However, this works instead: 대신 이 방법은 작동합니다:
await someAsyncFunction();
startTransition(() => {
// ✅ Setting state *during* startTransition call
setPage('/about');
});
I want to call useTransition
from outside a component컴포넌트 외부에서 useTransition
을 호출하고 싶습니다
You can’t call useTransition
outside a component because it’s a Hook. In this case, use the standalone startTransition
method instead. It works the same way, but it doesn’t provide the isPending
indicator.
훅이기 때문에 컴포넌트 외부에서 useTransition
을 호출할 수 없습니다. 이 경우 대신 독립형 startTransition
메서드를 사용하세요. 동일한 방식으로 작동하지만 isPending
표시기를 제공하지 않습니다.
The function I pass to startTransition
executes immediatelystartTransition
에 전달한 함수는 즉시 실행됩니다
If you run this code, it will print 1, 2, 3: 이 코드를 실행하면 1, 2, 3이 인쇄됩니다:
console.log(1);
startTransition(() => {
console.log(2);
setPage('/about');
});
console.log(3);
It is expected to print 1, 2, 3. The function you pass to startTransition
does not get delayed. Unlike with the browser setTimeout
, it does not run the callback later. React executes your function immediately, but any state updates scheduled while it is running are marked as Transitions. You can imagine that it works like this:
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 ...
}
}