useDeferredValue 는 UI의 일부분의 update (리렌더링)을 연기할 수 있는 Hook 이다.
const deferredValue = useDeferredValue(value)
useDeferredValue를 해석해보자면 연기된 값을 사용한다는 뜻이다.
value가 변하면서 컴포넌트가 리렌더링 될 때
바뀐 최신 value 를 쓰는 게 아니라
처음의 value를 갱신하는 것을 연기하여 계속 쓴다는 의미이다.
그렇다면 언제 리렌더링을 연기하여 deferredValue를 쓰면 좋을까?
첫 번째 예시
React 공식 문서의 예시를 가져왔다.
input에 검색할 앨범명을 입력하여 api 요청하고
그 결과를 유저에게 보여준다.
예를 들어, 유저가 abcde 이렇게 다섯 글자를 연속으로 입력한다.
이 때 마지막 입력이 끝난 abcde 에 대한 결과만 보여주고 싶을 경우
useDeferredValue 를 사용하면 된다.
어떻게 이렇게 동작하는 걸까?
공식 문서에 따르면 useDeferredValue 가 new value 를 받으면, background 에서 new value 를 이용한 re-render 가 발생한다. 이 background re-render는 interrupt 가 가능하다. 즉, ab를 입력하고 있을 때 a에 대해 리렌더링이 끝나지 않았더라도 b가 입력되었을 때 interrupt 가 발생한다. 그 결과 a의 리렌더링은 보여지지 않고 b의 리렌더링 결과만 보여지게 된다.
그림을 대충 그려봤는데
value가 a 일 때 리렌더링 하는 중에
b가 입력되어 interrupt 되어 b가 리렌더링 되고 ...
마지막에 e가 입력되었을 때 interrupt 되는 리렌더링이 없어서 e가 보여지게 되는 것이다.
렌더링 중이던 a는 어떻게 되나?
background 에서는 a와 ab 둘 다 리렌더링이 시도된다.
그러나 b가 interrupt 할 경우, 이 렌더링은 버려지게 되고, 데이터가 로드되었을 때 최신의 value 로 리렌더링을 하게 된다.
재밌는 것은 유저가 입력하여 받아온 response가 cache 로 저장되어
backspace로 이전에 호출했던 input 값인 경우
api 호출을 하지 않고 바로 사용한다고 한다.
그래서 공식 문서의 예시에 abc 를 입력하고 backspace 로 c를 지우면
로딩 없이 바로 ab의 결과를 보게 된다.
cache에 저장하지 않고 매번 호출하게 하는 방법도 있을까?
나중에 찾아보겠다.
※ Throttle이나 Debounce 와는 다르게 api 호출을 막진 않는다.
따라서 abcde 로 입력할 경우
a, ab, abc, abcd, abcde 로 api 호출은 발생한다.
UI에서 보여지는 것만 a, ab, abc, abd 를 안보여주고
abcde 만 보여주게 하는 것이다.
즉, api 호출에 대한 최적화를 위해서는 Throttle과 Debounce를 써야할 것이고
UI에 대한 최적화는 useDeferredValue 를 쓰면 된다.
두 번째 예시 : useDeferredValue 를 이용한 최적화
마찬가지로 공식 문서의 에시를 가져왔다.
[App.js]
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} />
</>
);
}
[SlowList.js]
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;
이 예시에서는 text 에 입력하여 SlowList 가 리렌더링 될 경우
의도적으로 딜레이를 줘서 리렌더링이 오래 걸리는 예시이다.
실제로 테스트 해보면 useDeferredValue 가 없을 경우 text 입력에 큰 딜레이가 발생하게 된다.
useDeferredValue를 사용하면 abcd 를 입력했을 때의 리렌더링 시간을 단축할 수 있다.
빨간색은 useDeferredValue 가 없을 경우이다.
a를 입력했을 때 SlowList 렌더링이 오래 걸려 b를 입력하지 못하고 오래 기다렸다가 입력해야한다.
파란색이 useDeferredValue를 사용한 경우인데
a를 입력하고 b를 바로 입력했을 때
a를 리렌더링 하는 중에 b가 interrupt 로 입력되어
b의 리렌더링을 수행하게 된다.
이로인해 input 을 곧바로 입력할 수 있게 되어 더 좋은 UX를 개발할 수 있다.
공식 문서 링크:
https://react.dev/reference/react/useDeferredValue
'dev' 카테고리의 다른 글
useEffect로 Race Condtions 해결하는 방법 (2) | 2023.11.22 |
---|---|
useCallback 심도있게 살펴보기 (0) | 2023.11.09 |
강원랜드 카지노 필승법 카드 카운팅 웹앱 개발 후기 (1) | 2023.10.05 |
jest 라이브러리 import 문제 해결 방법 (SyntaxError: Unexpected token 'export') (0) | 2023.08.13 |
Reactive Native Webview 의 장단점 (0) | 2023.07.19 |
댓글