Files
2nd/10_Wiki/Topics/Frontend/flushSync.md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
10_Wiki/Topics 대규모 정리:
- 오류 캡처/미완성 stub 문서 227개 제거
- 교차폴더 중복 43클러스터 병합 (63파일 → redirect)
- 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건
- 카테고리 MOC 6개 신규 생성
- Graph 섹션 미해결 related-keyword 링크 10,058건 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:52:15 +09:00

6.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-flushsync flushSync 10_Wiki/Topics verified self
flushSync
React flushSync
sync flush
none A 0.9 applied
react
react-dom
flushSync
batching
escape-hatch
2026-05-10 pending
language framework
typescript react-19

flushSync

매 한 줄

"매 React 18+ 의 automatic batching 우회 — 즉시 synchronous flush 강제". 매 React 18 의 default 가 모든 update 를 batch — 매 일부 case (third-party DOM lib, scrollIntoView after state change, focus management) 에서 즉시 flush 필요. 매 escape hatch — 매 misuse 시 perf 손해.

매 핵심

매 왜 필요한가

  • React 18 automatic batching: 매 setState 매 promises, timers, native event 까지 batch.
  • Pre-batch read pattern: 매 setState 후 즉시 DOM read/manipulation 가정한 legacy code 매 깨짐.
  • Third-party libs: 매 d3, jQuery plugin, chart lib 의 imperative DOM 의 sync state 필요.
  • scroll after mount: 매 list 의 새 item 추가 후 scrollIntoView — batch 하면 DOM 미반영.
  • focus after state change: 매 modal open + input focus — batch 하면 input 가 not yet rendered.

매 동작

  • flushSync(callback) — 매 callback 안의 모든 update 를 sync flush, 매 반환 직후 DOM 업데이트 완료.
  • 매 callback 외부의 다른 pending update 까지 함께 flush (warning 로그).
  • 매 expensive — 매 break 의 batching benefit (multiple re-renders).

매 응용

  1. List 추가 후 scrollIntoView (chat, log).
  2. Modal open + auto-focus input.
  3. d3/Canvas 의 React state → imperative draw.
  4. Print preview, screenshot 의 specific state 보장.
  5. Document.title sync update (browser tab 즉시 반영).

💻 패턴

Scroll to bottom — chat

import { flushSync } from 'react-dom';

function Chat() {
  const [messages, setMessages] = useState<string[]>([]);
  const containerRef = useRef<HTMLDivElement>(null);

  const addMessage = (msg: string) => {
    flushSync(() => {
      setMessages(prev => [...prev, msg]);
    });
    // 매 이 시점에 DOM 이미 update — scrollIntoView 가능
    containerRef.current?.scrollTo({
      top: containerRef.current.scrollHeight,
      behavior: 'smooth',
    });
  };

  return <div ref={containerRef}>{messages.map(...)}</div>;
}

Modal open + auto-focus

function App() {
  const [open, setOpen] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  const openModal = () => {
    flushSync(() => setOpen(true));
    // 매 modal 의 input 가 rendered — focus 가능
    inputRef.current?.focus();
  };

  return (
    <>
      <button onClick={openModal}>Open</button>
      {open && <Modal><input ref={inputRef} /></Modal>}
    </>
  );
}

Imperative Canvas / D3 sync

function Chart({ data }: { data: number[] }) {
  const svgRef = useRef<SVGSVGElement>(null);
  const [renderedData, setRenderedData] = useState(data);

  useEffect(() => {
    flushSync(() => setRenderedData(data));
    // 매 React 의 SVG 업데이트 후 d3 manipulation
    d3.select(svgRef.current).selectAll('rect').transition()...;
  }, [data]);

  return <svg ref={svgRef}>{/* React renders rects */}</svg>;
}

Print / Screenshot — specific state

const handlePrint = () => {
  flushSync(() => {
    setIsPrintMode(true); // 매 hide menu, expand all sections
  });
  window.print();
  setIsPrintMode(false); // 매 normal flush, no need flushSync
};

List item 추가 후 measure

function List() {
  const [items, setItems] = useState<Item[]>([]);
  const lastRef = useRef<HTMLLIElement>(null);

  const add = (item: Item) => {
    flushSync(() => setItems(prev => [...prev, item]));
    // 매 마지막 item 의 height 측정 가능
    const height = lastRef.current?.getBoundingClientRect().height;
    console.log('New item height:', height);
  };
  // ...
}

Document title sync

function NotificationCenter({ count }: { count: number }) {
  const incrementBadge = () => {
    flushSync(() => setCount(c => c + 1));
    // 매 doc.title 즉시 update — browser tab 표시
    document.title = `(${count + 1}) Inbox`;
  };
  // ...
}
// 매 alternative: useEffect 로 title sync — 더 idiomatic.

React 19 — useTransition 과 비교

const [pending, startTransition] = useTransition();

// 매 startTransition: low priority, batched, interruptible
startTransition(() => setBigList(newData));

// 매 flushSync: high priority, sync, immediate
flushSync(() => setBigList(newData));

// 매 둘 다 같이 사용 X — 의미 충돌.

매 결정 기준

상황 Approach
List 추가 후 scrollIntoView flushSync.
Modal open + focus flushSync + ref.focus().
Document.title sync useEffect (idiomatic) 또는 flushSync.
Performance optimization useTransition (반대 방향).
Simple state update 매 plain setState (자동 batch).

기본값: 매 setState (batching). 매 imperative DOM 의 즉시 sync 필요할 때만 flushSync.

🔗 Graph

🤖 LLM 활용

언제: chat scroll-to-bottom, modal auto-focus, third-party imperative DOM sync, list 추가 후 measure 의. 언제 X: 매 일반 state update (batch 가 더 빠름), useTransition 으로 충분한 case, 매 useEffect 로 처리 가능한 side effect.

안티패턴

  • 모든 setState 에 flushSync: 매 batching benefit 소멸 — 매 perf 저하.
  • render 중 호출: 매 flushSync inside render — error. 매 event handler / effect 에서만.
  • async 안에서: 매 await fetch(); flushSync(() => setX()) — 매 동작하나 매 batching 의 의미 없음 (async 가 이미 micro-task break).
  • useEffect 대체로 사용: 매 flushSync 매 sync flush 강제 — useEffect 의 lifecycle 의 대체 X.
  • Suspense 의 children update: 매 Suspense boundary 의 sync flush 매 hydration mismatch 위험.

🧪 검증 / 중복

  • Verified (React docs flushSync, React 18 release notes, Dan Abramov 의 RFC).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — flushSync use-cases, scroll/focus patterns, useTransition 비교 추가