- useDeferredValue : UI 일부의 업데이트를 지연시킬 수 있는 React 훅이다.
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
- 업데이트가 발생하면, 지연된 값은 최신 값보다 "뒤쳐지게" 됩니다.
- React는 먼저 지연된 값을 업데이트하지 않은 채로 렌더링한 다음, 백그라운드에서 새로 받은 값으로 다시 렌더링을 시도한다.
import { Suspense, useState } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={query} />
</Suspense>
</>
);
}
- 위 예시에서 input 창에 "a"를 입력하고 결과를 기다린 다음 "ab"로 수정하는 경우 "a"에 대한 결과가 로딩 폴백으로 대체된다.
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}
- 위와 같이 useDeferredValue를 사용하면 "a"를 입력하고 결과를 기다린 다음 "ab"로 수정하는 경우 "a"에 대한 결과가 로딩 폴백으로 대체되지 않고, "a"에 대한 검색 결과 목록을 표시한다.
How does deferring a value work under the hood?(값을 지연시키는 것은 어떻게 작동하나요?)
- useDeferredValue는 두 단계로 진행이 된다.
1. 위 예제에서 먼저 React는 query("ab")가 아닌 이전 deferredQuery(여전히 "a")로 다시 렌더링한다.
-> "a"에 대한 것으로 렌더링을 한번 다시 하긴 한다는 말인가?
2. 백그라운드에서 React는 query와 deferredQuery를 모두 "ab"로 업데이트한 상태로 리렌더링을 시도한다.
- 만약 "ab" 검색결과에 대한 작업이 일시 중단되면, React는 이 렌더링 시도를 포기하고 데이터가 로드된 후 이 렌더링을 다시 시도한다.
- 지연된 백그라운드 렌더링은 중단할 수 있다. 예를 들어, 사용자가 입력을 다시 시도하면 React는 해당 입력을 버리고 새 값으로 다시 시작한다. 이때 React는 항상 가장 최근에 제공받은 값을 사용한다.
- 다만, 각 키 입력마다 네트워크 요청은 여전히 존재한다. 즉, 지연되는 것은 네트워크 요청 자체가 아니라 결과가 준비될 때까지 결과를 표시하는 것이다.
- 사용자가 계속 입력하더라도 각 키 입력에 대한 응답은 캐시되므로 백스페이스를 누르면 즉시 다시 가져오지는 않는다.
Deferring re-rendering for a part of the UI(UI의 일부에 대해 리렌더링 연기하기)
- useDeferredValue를 성능 최적화로 적용할 수도 있다.
- 만약 UI의 일부가 리렌더링 속도가 느릴 때, 나머지 UI를 차단하지 않도록 하려는 경우에 유용하다.
- 예를 들어, 사용자의 입력에 따라, 결과 목록을 업데이트해야 하는 경우에, 결과 목록 업데이트보다 입력 업데이트의 우선 순위를 지정할 수 있다.
// 입력 업데이트의 우선순위를 지정
// input에 타이핑하면 타이핑은 빠르게 느껴지지만, 목록은 "지연"되는 것을 확인할 수 있다.
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';
export default function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}
// useDeferredValue가 없으면, 키 입력시마다 전체 목록이 중단되지 않는 방식으로 즉시 다시 리렌더링 된다.
// 따라서, input에 타이핑할 때 매우 뻑뻑한 느낌이 든다.
import { useState } from 'react';
import SlowList from './SlowList.js';
export default function App() {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={text} />
</>
);
}
- 다만 위와 같은 최적화를 위해서는 SlowList를 memo로 감싸야한다.
- 텍스트가 변경될 때마다 React가 부모 컴포넌트를 빠르게 다시 렌더링해야 하기 때문이다.
- 다시 렌더링하는 동안 deferredText는 여전히 이전 값을 가지므로 SlowList는 리렌더링을 건너뛸 수 있다.
- 만약 memo가 없다면 어쨌든 다시 렌더링해야 하므로 최적화의 취지가 무색해진다.
import { memo } from 'react';
const SlowList = memo(function SlowList({ text }) {
// Log once. The actual slowdown is inside SlowItem.
console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');
let items = [];
for (let i = 0; i < 250; i++) {
items.push(<SlowItem key={i} text={text} />);
}
return (
<ul className="items">
{items}
</ul>
);
});
function SlowItem({ text }) {
let startTime = performance.now();
while (performance.now() - startTime < 1) {
// Do nothing for 1 ms per item to emulate extremely slow code
}
return (
<li className="item">
Text: {text}
</li>
)
}
export default SlowList;
How is deferring a value different from debouncing and throttling?(값을 연기하는 것은 디바운스 및 쓰로틀과 어떤 점이 다른가요?)
- 디바운스 : 사용자가 타이핑을 멈출 때까지 기다렸다가 목록을 업데이트하는 것을 의미
- 쓰로톨 : 가끔씩(예: 최대 1초에 한 번) 목록을 업데이트하는 것을 의미
- useDeferredValue는 React 자체와 깊게 통합되어 있고 사용자의 기기에 맞게 조정되기 때문에 렌더링을 최적화하는데 더 적합하다.
- 또한 디바운스나 쓰로틀과 달리 useDeferredValue에 의해 수행되는 지연된 리렌더링은 기본적으로 중단이 가능하다. 반면, 디바운스나 쓰로틀은 렌더링이 키 입력을 차단하는 순간을 연기할 뿐이다.
- 대신 디바운스나, 쓰로틀을 사용하면 네트워크 요청을 더 적게 실행할 수 있다.
* 참고 : React 공식문서(https://react-ko.dev/learn)
'이것저것 스터디📚 > React - 공식 문서' 카테고리의 다른 글
React 공식문서 -<Profiler> (0) | 2023.10.11 |
---|---|
React 공식문서 -<StrictMode> (1) | 2023.10.11 |
React 공식문서 -useInsertionEffect (0) | 2023.10.05 |
React 공식문서 -useLayoutEffect (0) | 2023.10.04 |
React 공식문서 -useTransition (0) | 2023.10.04 |