개요
useDeferredValue
는 UI의 일부 업데이트를 지연(Deferred)시킬 수 있게 해주는 React Hook입니다.
const deferredValue = useDeferredValue(value)
Reference
useDeferredValue(value, initialValue?)
컴포넌트의 최상위에서 useDeferredValue
를 호출하면, 해당 값의 지연된 버전을 얻을 수 있습니다.
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
파라미터
value
: 지연시키고 싶은 값. 어떤 타입이든 가능.initialValue
(선택): 컴포넌트의 최초 렌더 시 사용할 값. 생략하면 최초 렌더에서는 지연이 적용되지 않습니다.
반환값
currentValue
: 최초 렌더에서는initialValue
또는 전달한 값 자체를 반환합니다. 업데이트 시에는 이전 값을 먼저 반환하고, 백그라운드에서 새 값으로 다시 렌더를 시도합니다.
주의사항
- 트랜지션(Transition) 내에서 업데이트가 발생하면, useDeferredValue는 항상 새 값을 반환하며 별도의 지연 렌더를 생성하지 않습니다.
- 전달하는 값은 원시값(문자열, 숫자 등)이나 렌더링 외부에서 생성된 객체여야 합니다. 렌더링 중에 새 객체를 만들어 바로 전달하면, 매 렌더마다 값이 달라져 불필요한 백그라운드 렌더가 발생할 수 있습니다.
- 값이 바뀌면(비교는 Object.is) 현재 렌더에서는 이전 값을 사용하고, 백그라운드에서 새 값으로 렌더를 시도합니다. 이 백그라운드 렌더는 인터럽트(중단)될 수 있습니다.
- Suspense와 통합되어 있습니다. 백그라운드 렌더가 suspend되면, fallback이 아닌 이전 값을 계속 보여줍니다.
- useDeferredValue 자체는 네트워크 요청을 지연시키지 않습니다.
- useDeferredValue는 고정된 지연 시간을 두지 않습니다. 원래 렌더가 끝나면 즉시 백그라운드 렌더를 시작합니다.
- 백그라운드 렌더가 커밋되기 전까지 Effect는 실행되지 않습니다.
사용법
새 데이터가 로딩되는 동안 이전(오래된) 콘텐츠 보여주기
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
업데이트 시, deferred 값은 최신 값보다 "한 박자 느리게" 바뀝니다. React는 먼저 이전 값으로 렌더링한 뒤, 백그라운드에서 새 값으로 다시 렌더를 시도합니다.
최신 쿼리와 결과가 다를 때 시각적으로 표시하기
deferred 값이 최신 값과 다를 때, 예를 들어 opacity를 낮추는 등 시각적으로 "로딩 중"임을 표시할 수 있습니다.
const isStale = query !== deferredQuery;
return (
<div style={{ opacity: isStale ? 0.5 : 1 }}>
<SearchResults query={deferredQuery} />
</div>
);
UI 일부의 리렌더링만 지연시키기 (성능 최적화)
입력 필드와 같이 빠른 반응이 필요한 UI와, 느리게 렌더링되는 컴포넌트(예: 대용량 리스트)를 분리할 때 유용합니다.
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}
이렇게 하면 입력 필드는 즉시 반응하고, 리스트는 입력값을 따라가며 "한 박자 느리게" 업데이트됩니다. 리스트가 느리게 렌더링되어도 입력이 끊기지 않습니다.
주의: 이 최적화는 SlowList가 memo로 감싸져 있어야 효과가 있습니다. memo가 없으면 prop이 바뀔 때마다 무조건 리렌더링됩니다.
useDeferredValue와 디바운스/스로틀의 차이
- 디바운스(debounce): 사용자가 입력을 멈춘 뒤 일정 시간 후에만 업데이트
- 스로틀(throttle): 일정 시간마다 한 번씩만 업데이트
- useDeferredValue: React가 렌더링 우선순위를 조절하여, 입력 등 중요한 작업이 먼저 처리되고, 느린 컴포넌트는 백그라운드에서 따라오게 함. 고정된 지연 시간이 없고, 사용자의 기기 성능에 따라 반응성이 달라짐. 백그라운드 렌더는 언제든 인터럽트될 수 있음.
반응형