--- id: wiki-2026-0508-usetransition title: useTransition category: 10_Wiki/Topics status: verified canonical_id: self aliases: [React useTransition, Concurrent Transitions, startTransition Hook] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [react, hook, concurrent, performance, react-19] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: React 19 --- # useTransition ## 매 한 줄 > **"매 useTransition 의 핵심 = 'state update 의 non-urgent 로 mark 의 UI 의 freeze 의 prevent'."**. React 18 (2022) 의 introduction 의 since 매 concurrent rendering 의 user-facing API. 2026 React 19 era 매 Actions 의 native integration 의 하여 매 async server mutation 의 also 의 cover. ## 매 핵심 ### 매 Signature ```typescript const [isPending, startTransition] = useTransition(); // startTransition: (action: () => void | Promise) => void // isPending: boolean — true while transition in progress ``` ### 매 Mechanism - **Urgent updates**: typing, click, hover — 매 immediate render. - **Transition updates**: filter, search, navigation — 매 startTransition 의 wrap → low priority. - React 의 scheduler 의 urgent first 의 process, transition 의 interrupt 의 가능 if 매 새 urgent update 의 arrive. - **isPending**: 매 spinner / skeleton 의 show 의 used. ### 매 React 19 추가 1. **Async actions**: `startTransition(async () => { await save(); })` 의 native support. 2. **useActionState**: form action 의 transition 의 자동 wrap. 3. **useOptimistic**: optimistic UI 의 transition pending 동안. ## 💻 패턴 ### Pattern 1: Search filter (urgent input + transition list) ```typescript import { useState, useTransition } from 'react'; function SearchableList({ items }: { items: string[] }) { const [query, setQuery] = useState(''); const [filteredItems, setFilteredItems] = useState(items); const [isPending, startTransition] = useTransition(); const handleChange = (e: React.ChangeEvent) => { const value = e.target.value; setQuery(value); // urgent startTransition(() => { // expensive filter — non-urgent setFilteredItems(items.filter(i => i.toLowerCase().includes(value.toLowerCase()))); }); }; return ( <> {isPending && Filtering...} ); } ``` ### Pattern 2: Tab switching (heavy children) ```typescript function TabContainer() { const [tab, setTab] = useState<'home' | 'reports' | 'analytics'>('home'); const [isPending, startTransition] = useTransition(); const switchTab = (next: typeof tab) => { startTransition(() => setTab(next)); }; return ( <>
{tab === 'home' && } {tab === 'reports' && } {tab === 'analytics' && }
); } ``` ### Pattern 3: React 19 async action ```typescript function SaveButton({ data }: { data: FormData }) { const [isPending, startTransition] = useTransition(); const [error, setError] = useState(null); const handleSave = () => { startTransition(async () => { try { await fetch('/api/save', { method: 'POST', body: data }); } catch (e) { setError((e as Error).message); } }); }; return ( <> {error &&

{error}

} ); } ``` ### Pattern 4: useActionState (React 19) 의 wrap ```typescript import { useActionState } from 'react'; async function submitForm(prev: State, formData: FormData): Promise { const res = await fetch('/api/contact', { method: 'POST', body: formData }); return res.ok ? { ok: true } : { ok: false, error: 'Failed' }; } function ContactForm() { const [state, action, isPending] = useActionState(submitForm, { ok: false }); // useActionState 의 internally 의 useTransition 의 use return (
{state.error &&

{state.error}

}
); } ``` ### Pattern 5: Standalone startTransition (no isPending needed) ```typescript import { startTransition } from 'react'; // outside component — useful for event handlers in stores function navigateTo(path: string) { startTransition(() => { router.push(path); }); } ``` ### Pattern 6: Combine 의 with Suspense ```typescript function ProductPage({ id }: { id: string }) { const [currentId, setCurrentId] = useState(id); const [isPending, startTransition] = useTransition(); return ( <> }> {isPending && } ); } // Suspense 의 fallback 의 X show — old UI 의 stays 의 while transition pending ``` ### Pattern 7: Prevent unwanted suspense boundaries ```typescript // useDeferredValue 의 vs useTransition decision: // - You control where state is set → useTransition // - State 의 from props / external → useDeferredValue const [isPending, startTransition] = useTransition(); startTransition(() => setQuery(input)); // I own setQuery ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Heavy filter / sort 의 input 의 reactive | useTransition | | Tab / route switching with heavy children | useTransition + Suspense | | Async server mutation (React 19) | useTransition (async) or useActionState | | State 의 owned externally (props) | useDeferredValue | | Throttle / debounce | setTimeout — useTransition 의 X | **기본값**: useTransition for any expensive state update 의 user-initiated. ## 🔗 Graph - 부모: [[Concurrent-React]] - 변형: [[useDeferredValue]] · [[useOptimistic]] - Adjacent: [[Suspense]] ## 🤖 LLM 활용 **언제**: refactor expensive synchronous update 의 transition / migrate React 18→19 의 useActionState 의 detection. **언제 X**: 매 actual user perception 의 measurement 의 X — 매 React DevTools Profiler 의 use. ## ❌ 안티패턴 - **Wrap urgent updates**: input value setter 의 transition 의 wrap → laggy typing. - **Throttle replacement**: useTransition 의 throttle 의 X — 매 single batch 의 latest 의 commit, not 의 rate-limit. - **Side effects in startTransition**: API call 의 directly (React 18) — React 19 async action 의 use. - **Forgetting isPending**: 매 user feedback 의 missing → perceived freeze. - **Wrap synchronous side effects**: localStorage write 의 transition 의 wrap 의 useless. ## 🧪 검증 / 중복 - Verified: react.dev/reference/react/useTransition (React 19), React 19 release notes. - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — canonical useTransition reference (React 19) |