Files
2nd/10_Wiki/Topics/Frontend/Concurrent Features.md
T
2026-05-10 22:08:15 +09:00

5.4 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-concurrent-features Concurrent Features (React 18+) 10_Wiki/Topics verified self
React Concurrent Mode
useTransition
useDeferredValue
none A 0.9 applied
react
concurrent
performance
frontend
2026-05-10 pending
language framework
TypeScript React 19

Concurrent Features (React 18+)

매 한 줄

"매 render 매 interruptible 하다". React 18 부터 매 concurrent renderer — useTransition, useDeferredValue, Suspense, automatic batching, streaming SSR 매 모두 매 high-priority update 가 low-priority 를 preempt 가능한 architecture 위에 build.

매 핵심

매 핵심 features

  • Automatic batching: promise / setTimeout / native event 모두 batch.
  • startTransition / useTransition: 매 non-urgent state update marking.
  • useDeferredValue: input 매 lag 없이 expensive list 매 deferred.
  • Suspense: data fetching boundary, fallback UI.
  • Streaming SSR: renderToPipeableStream (Node), renderToReadableStream (Edge).
  • useId: SSR-safe unique id.
  • use() hook (React 19): unwrap promise/context inside render.

매 priority levels

  1. Sync / urgent (user input).
  2. Default (most state updates).
  3. Transition (startTransition).
  4. Idle.

매 응용

  1. 매 typeahead search — input snappy + results deferred.
  2. Tab switching — 매 stale UI keeps interactive.
  3. Streaming page render — 매 fastest paint then progressively fill.

💻 패턴

useTransition for non-urgent updates

import { useState, useTransition } from 'react';

function Filter({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('');
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();

  const onChange = (v: string) => {
    setQuery(v); // urgent — input responds immediately
    startTransition(() => {
      setFiltered(items.filter((i) => i.name.includes(v))); // non-urgent
    });
  };

  return (
    <>
      <input value={query} onChange={(e) => onChange(e.target.value)} />
      {isPending && <Spinner />}
      <List items={filtered} />
    </>
  );
}

useDeferredValue

import { useState, useDeferredValue, memo } from 'react';

function Search({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('');
  const deferred = useDeferredValue(query);
  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <SlowList items={items} query={deferred} />
    </>
  );
}

const SlowList = memo(({ items, query }: { items: Item[]; query: string }) => {
  return <ul>{items.filter((i) => i.name.includes(query)).map((i) => <li key={i.id}>{i.name}</li>)}</ul>;
});

Suspense boundary

import { Suspense } from 'react';

function App() {
  return (
    <Suspense fallback={<Skeleton />}>
      <UserProfile id={42} />
      <Suspense fallback={<PostsSkeleton />}>
        <Posts userId={42} />
      </Suspense>
    </Suspense>
  );
}

use() hook (React 19)

import { use, Suspense } from 'react';

function Profile({ promise }: { promise: Promise<User> }) {
  const user = use(promise); // 매 unwrap inside render
  return <h1>{user.name}</h1>;
}

// caller
<Suspense fallback={<Spinner />}>
  <Profile promise={fetchUser(42)} />
</Suspense>

Streaming SSR (Node)

import { renderToPipeableStream } from 'react-dom/server';

server.get('/', (req, res) => {
  const { pipe } = renderToPipeableStream(<App />, {
    bootstrapScripts: ['/main.js'],
    onShellReady() { res.statusCode = 200; pipe(res); },
    onError(err) { console.error(err); },
  });
});

Automatic batching across async

function handleClick() {
  // React 18+: 매 둘 다 single render
  setTimeout(() => {
    setCount((c) => c + 1);
    setFlag((f) => !f);
  }, 0);
}

flushSync for opt-out

import { flushSync } from 'react-dom';

flushSync(() => setItems(next)); // 매 sync flush — DOM ready
scrollToBottom(); // 매 새 item 매 already mounted

매 결정 기준

상황 Feature
Slow filter blocks input useTransition or useDeferredValue
Async data in component tree Suspense + use()
Need DOM after update flushSync
SSR slow first byte streaming SSR
Pre-React-18 codebase upgrade first

기본값: input UI 매 lag → useDeferredValue. Bigger state cascade → useTransition.

🔗 Graph

🤖 LLM 활용

언제: 매 input lag, expensive list, async-heavy tree, SSR perf. 언제 X: 매 trivial UI, 매 add-only sync state.

안티패턴

  • startTransition for urgent input: 매 input 매 still feel laggy.
  • No Suspense boundary 위 use(): 매 throws 매 above-tree fallback 까지 propagate.
  • Mixing flushSync everywhere: 매 defeats concurrent benefits.

🧪 검증 / 중복

  • Verified (react.dev official docs).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — React 18/19 concurrent features + patterns