useDeferredValue(value)는 값의 "지연된 버전"을 돌려주는 훅이다. 값이 바뀌면 React는 먼저 이전 값으로 긴급한 부분을 렌더하고, 새 값으로의 렌더는 낮은 우선순위로 백그라운드에서 한다.
const [query, setQuery] = useState('')
const deferredQuery = useDeferredValue(query)
<input value={query} onChange={e => setQuery(e.target.value)} /> // 즉시
<SearchResults query={deferredQuery} /> // 늦게
const SearchResults = memo(function SearchResults({ query }) { /* 무거움 */ })memo와 같이 써야 효과가 난다. 무거운 컴포넌트가 지연된 값에만 의존하고memo로 감싸져 있어야 입력이 바뀌는 동안 다시 렌더되지 않는다(memo·useMemo·useCallback)query !== deferredQuery이면 "갱신 중"이니 결과를 흐리게 보여 줄 수 있다. React 19부터 두 번째 인자로 첫 렌더용 초기값을 줄 수 있다
디바운스(Debounce)와 다른 점
| 디바운스 | useDeferredValue | |
|---|---|---|
| 기준 | 시간(마지막 입력 후 300ms 등) | 우선순위 |
| 기기 차이 | 빠른 기기도 똑같이 기다림 | 빠른 기기는 거의 지연 없음 |
| 중단 | 타이머가 울리면 끝까지 렌더 | 새 값이 오면 진행 중 렌더를 버림 |
| 지연값 튜닝 | 필요 | 불필요 |
- 네트워크 요청 수는 줄이지 못한다. "API(Application Programming Interface)를 N초에 한 번만" 같은 부수 효과 빈도 제어는 여전히 디바운스·스로틀(Throttle)이 맞다. 서버 쪽 제한은 요청 제한 (Throttling)
- 이미 가진 값으로 하는 무거운 렌더를 매끄럽게 하는 도구다. 데이터 요청 지연에는 Suspense·useTransition이 더 맞는 경우가 많다
setState를 직접 감쌀 수 있으면useTransition, props처럼 값만 받으면useDeferredValue. 내부 메커니즘은 같다(동시성 렌더링)