(null);
useEffect(() => {
const obs = new IntersectionObserver(([e]) => {
if (e.isIntersecting) { setVisible(true); obs.disconnect(); }
});
if (ref.current) obs.observe(ref.current);
return () => obs.disconnect();
}, []);
return {visible ? children : null}
;
}
```
### Virtualized list (TanStack Virtual)
```tsx
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 40,
});
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Typing → expensive render | useDeferredValue |
| Tab/route 전환 heavy | useTransition |
| Below-fold heavy section | content-visibility: auto |
| 1000+ row list | TanStack Virtual |
| Code split heavy editor | React.lazy + Suspense |
**기본값**: useDeferredValue (typing) + useTransition (navigation) + content-visibility (off-screen).
## 🔗 Graph
- 부모: [[Concurrent Features|Concurrent Rendering]]
- 변형: [[useTransition]] · [[useDeferredValue]] · [[Suspense]]
- 응용: [[Virtualization]]
- Adjacent: [[Code Splitting]] · [[Critical Rendering Path (CRP)|Critical Rendering Path]]
## 🤖 LLM 활용
**언제**: 매 input lag, 매 INP > 200ms, 매 large list, 매 heavy chart.
**언제 X**: 매 small static UI, 매 SSR-only.
## ❌ 안티패턴
- **모든 update 를 transition 으로**: 매 critical update 도 지연 → 매 stale UI.
- **useDeferredValue + heavy memo 부재**: 매 child 가 매 매번 재렌더.
- **Suspense 없이 React.lazy**: 매 fallback 미정 → crash.
## 🧪 검증 / 중복
- Verified (React 19 docs, web.dev INP guide).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — deferred rendering 7 patterns |