
Suspense 글의 마무리에서 예고했던 내용을 되돌아보자.
검색어를 입력하는 상황에서 '사' 라고 입력하고 다음 글자로 '과'를 입력하는데 지연이 발생했다고 하자.
자동완성기능에 '사'로 시작하는 단어들이 나왔다가 '과'를 입력하니까 '로딩중...' 이라고 렌더링이 됐다면?
사용자는 😲"이게뭐지?" 하며 어리둥절해할 것이고, 결국 사용자경험(UX)도 좋지않을 것이다.
이럴 때는 '사'라고 입력했을 때의 결과. 즉, 이전 값을 유지한채 '과'에 대한 결과값 로딩을 기다리는 것이 사용자경험 측면에서 더 좋은 방향이다.
위 예고편의 문제(사용자 경험 저하)를 해결하는 방법 중 하나로 memo 함수로 느리게 렌더링되는 컴포넌트를 감싸는 방법이 있다.
이 방법으로 불필요한 렌더링을 줄일 순 있으나, 입력값이라는 props는 변경되지 않았으므로 여전히 렌더링이 느려 근본적인 해결책이 되기엔 아쉬운 부분이 있다.
| 일부 UI 업데이트 반응속도를 지연시킬 수 있는 리액트 훅이다.
// 형태
const deferredValue = useDeferredValue(value, initialValue?)
value
(optional) initialValue
undefined 가 아닌 설정한 값으로 시작할 수 있게 되었다.currentValue
지연된 값은 사용자가 제공한 값과 동일하다.이러한 useDeferredValue 의 대표적인 사용예시는 다음과 같으며, 이 예시를 생각하면서 내용을 살펴보자.
import { useState } from 'react'
import SlowChild from './slow-child'
export default function App() {
const [query, setQuery] = useState('')
return (
<div className="app">
<label htmlFor="search-input">
검색어 입력
</label>
<input
id="search-input"
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="검색어를 입력하세요."
/>
<SlowChild query={query} />
</div>
)
}
// SlowChild.tsx
export default function SlowChild({ query }) {
// start 변수 컴포넌트가 렌더링될 때 시간(ms) 저장
const start = performance.now()
// 현재 시간에서 start 변수 값을 뺀 값이
// 100ms보다 작을 동안 아무 작업도 하지 않고 계속 반복
// 즉, 아무 일도 않고 100ms 동안 CPU를 점유
while (performance.now() - start < 100) {
// 성능 저하 시뮬레이션
}
return <p>검색어: {query}</p>
}
Suspense로 감싼 컴포넌트를 렌더링할 때 새로운 검색어를 입력하면 '로딩 중...'만 보여주었다.
하지만 useDeferredValue와 함께라면, 새 데이터가 준비될 때까지 이전 데이터(지연된 값)를 렌더링할 수 있다.
✋🏻잠깐! 그럼 Suspense를 굳이 왜 쓰는거야?
useDeferredValue의 반환값인지연된 값과입력값이 다를 수가 있는데, 이 때는 보여줄 이전 데이터가 없어진다.
왜냐면useDeferredValue훅은 별도의 로딩 상태를 제공하지않기 때문이다.
이럴 때 짜잔-! 하고 보여주려고 Suspense 가 든든하게 컴포넌트를 감싸고 대기중인 것이다. (마니또 느낌)
개발자도구-Profiler 를 통해서 확인해야한다.✋🏻잠깐! 쓰로틀(Throttle) 과 디바운스(Debounce)가 뭔데?
곧 내용 업데이트 예정입니다
캐싱을 적용하면 불필요한 네트워크 요청과 렌더링을 줄이는게 효과적이다. (useDeferredValue 은 그 자체로 추가 네트워크 요청 방지를 하지 않는다.) useDeferredValue 로 인한 백그라운드 리렌더링이 화면에 커밋 될 때까지 Effect 는 실행되지 않는다.'바' 입력 → '나' 입력 → '나' 지우고 '보' 입력 이렇게 수정입력했다고하면 리액트는 '바나' 까지를 지연된 값으로 저장해놨다가 '나' 를 지우고 '보' 를 입력하는 순간 저장해둔 '바나' 를 지우고 새로운 값인 '바보'로 지연된 값을 업데이트 하는 것이다.1. useDeferredValue 의 사용목적은 분리 이다.
2. memo 와 같이 사용해야 최적화가 이루어진다.
memo 로 감싸지 않을 경우, 입력값이 변경될 때마다 부모 컴포넌트가 리렌더링 되어야 한다.props 변경까지 memo 가 캐치해줄 수는 없지만, memo 마저 없으면 최적화의 취지가 무색해진다.🔗 참고자료
https://ko.react.dev/reference/react/useDeferredValue