--- id: wiki-2026-0508-flushsync title: flushSync category: 10_Wiki/Topics status: verified canonical_id: self aliases: [flushSync, React flushSync, sync flush] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [react, react-dom, flushSync, batching, escape-hatch] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: 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 ```tsx import { flushSync } from 'react-dom'; function Chat() { const [messages, setMessages] = useState([]); const containerRef = useRef(null); const addMessage = (msg: string) => { flushSync(() => { setMessages(prev => [...prev, msg]); }); // 매 이 시점에 DOM 이미 update — scrollIntoView 가능 containerRef.current?.scrollTo({ top: containerRef.current.scrollHeight, behavior: 'smooth', }); }; return
{messages.map(...)}
; } ``` ### Modal open + auto-focus ```tsx function App() { const [open, setOpen] = useState(false); const inputRef = useRef(null); const openModal = () => { flushSync(() => setOpen(true)); // 매 modal 의 input 가 rendered — focus 가능 inputRef.current?.focus(); }; return ( <> {open && } ); } ``` ### Imperative Canvas / D3 sync ```tsx function Chart({ data }: { data: number[] }) { const svgRef = useRef(null); const [renderedData, setRenderedData] = useState(data); useEffect(() => { flushSync(() => setRenderedData(data)); // 매 React 의 SVG 업데이트 후 d3 manipulation d3.select(svgRef.current).selectAll('rect').transition()...; }, [data]); return {/* React renders rects */}; } ``` ### Print / Screenshot — specific state ```tsx const handlePrint = () => { flushSync(() => { setIsPrintMode(true); // 매 hide menu, expand all sections }); window.print(); setIsPrintMode(false); // 매 normal flush, no need flushSync }; ``` ### List item 추가 후 measure ```tsx function List() { const [items, setItems] = useState([]); const lastRef = useRef(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 ```tsx 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 과 비교 ```tsx 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 - 부모: [[React]] - 변형: [[useTransition]] · [[startTransition]] · [[Automatic Batching]] - Adjacent: [[Batching]] ## 🤖 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 비교 추가 |