C.W.K.
Stream
Lesson 01 of 06 · published

useTransition — 비긴급 state 업데이트

~12 min · usetransition, concurrent, priority

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
일부 state 업데이트는 즉시 느껴져야 (키 입력). 다른 건 잠시 걸려도 됨 (10,000 검색 결과 렌더). useTransition 이 React 한테 어느 게 어느 건지 알려줘 — 긴급한 게 비긴급한 거 안 기다리게.

모양

const [isPending, startTransition] = useTransition(). startTransition(() => setX(...)) 안에 어떤 state 업데이트든 감싸면 React 가 저우선순위로 태그. Hook 이 어떤 in-flight transition 이 여전히 도는지 알려줌.

전형적 사용 케이스 — 검색

검색 input 타이핑은 항상 즉시 느껴져야. 다만 새 쿼리로 결과 리스트 업데이트가 무거운 컴포넌트 트리 re-render 가능. Transition 없으면 'react' 타이핑이 키 입력당 수십 밀리초 막음. 결과 업데이트 감싸는 transition 으로 input 즉시 업데이트 (긴급), 결과가 백그라운드에서 catch up (비긴급).

Suspense 상호작용

Transition 이 Suspense fallback 도 smooth. State 변경이 자식 suspend 일으키면 보통 Suspense fallback 이 flash in. Transition 안에선 React 가 새 거 준비될 때까지 이전 컨텐츠 보이게 유지 — fallback flash 없음. isPending 이 페이지 blank 안 하고 subtle pending 지시자 (예: 상단 로딩 바) 언제 보일지 알려줌.

startTransition 안에 들어가는 것

State 업데이트만. Fetch, side effect, 또는 어떤 async 도 감싸지 마. 패턴: 작업 수행, 그 다음 state 변경을 transition 으로 표시:

const data = await fetchResults(query);
startTransition(() => setResults(data));
우선순위가 커뮤니케이션. React 가 어느 업데이트가 긴급인지 추론 못 함 — 본인만 앎. useTransition 이 알려주는 방법. Transition 전에 존재한 hook (useState, useEffect) 은 모든 업데이트 똑같이 긴급 취급. 그 가정이 React 가 큰 트리에서 janky 하게 느껴지게 한 원인.

Code

Transition 가진 검색 — input 이 반응 유지·tsx
import { useState, useTransition } from "react";

function SearchPage() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<string[]>([]);
  const [isPending, startTransition] = useTransition();

  function onInput(value: string) {
    setQuery(value); // 긴급 — input 즉시 업데이트
    startTransition(() => {
      // 비긴급 — 10k 결과 렌더가 타이핑 안 막음
      setResults(filterMassiveDataset(value));
    });
  }

  return (
    <div>
      <input value={query} onChange={(e) => onInput(e.target.value)} />
      {isPending && <p className="text-muted text-sm">Updating…</p>}
      <ul style={{ opacity: isPending ? 0.6 : 1 }}>
        {results.map((r) => <li key={r}>{r}</li>)}
      </ul>
    </div>
  );
}

function filterMassiveDataset(_q: string): string[] {
  // 상상: 10,000 row, 필터링 + 정렬.
  return [];
}
Transition 이 Suspense smooth — fallback flash 없음·tsx
import { Suspense, useState, useTransition } from "react";

function TabbedView() {
  const [tab, setTab] = useState<"a" | "b">("a");
  const [isPending, startTransition] = useTransition();

  function switchTab(next: "a" | "b") {
    // startTransition 없으면: 탭 스위치가 Suspense fallback flash.
    // 있으면: 새 탭 데이터 resolve 까지 이전 탭 보이게 유지.
    startTransition(() => setTab(next));
  }

  return (
    <div>
      <nav className="flex gap-2" style={{ opacity: isPending ? 0.6 : 1 }}>
        <button onClick={() => switchTab("a")}>A</button>
        <button onClick={() => switchTab("b")}>B</button>
      </nav>
      <Suspense fallback={<p>Loading…</p>}>
        {tab === "a" ? <TabA /> : <TabB />}
      </Suspense>
    </div>
  );
}

function TabA() { return <p>A</p>; }
function TabB() { return <p>B</p>; }

External links

Exercise

Input 타이핑이 5000 아이템 리스트 필터하는 리스트 필터 빌드. useTransition 없으면 타이핑 시 input lag. 필터 업데이트 주변에 useTransition 추가하고 관찰: 타이핑이 즉시 유지, 필터된 결과가 smoothly catch up. isPending 플래그 써서 transition 중 리스트 dim → 사용자가 in-flight state 봄.
Hint
5000 아이템 생성: Array.from({ length: 5000 }, (_, i) => item-${i}). 필터는 items.filter(i => i.includes(query)). Transition 없으면 per-키스트로크 필터가 input 막음.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.