
들어가는 말
이 글은 리액트 공식 문서의 Built-in React Hooks 부분에 등장한 훅들의 공식 문서를 훑어보고, 각 훅을 어떤 상황에서 사용하면 좋은지를 정리한 글입니다.
useState
- 리액트의 상태를 사용하고 싶을 때
- 상태란?
- 앱이 기억해야 하는 최소한의 변하는 자료
- 상태는 결국 ui의 다양한 상태를 표현하기 위한 것 → 상태의 변화는 곧 ui의 변화이므로 리렌더링
useReducer
- 많은 이벤트 핸들러에 다양한 상태 업데이트들이 퍼져 있어서 복잡할 때
- 모든 상태 업데이트 로직을 컴포넌트 밖으로 빼서 reducer라는 하나의 순수함수로 변형 가능
- 상태 업데이트 코드에 확실한 구조를 도입하고 싶을 때
- 컴포넌트 상태 업데이트 과정에서 추적이 귀찮은 버그를 자주 만날 때
- (개인 의견) 여기서 말한 문제를 reducer로 해결할 수 있을지 잘 모르겠음
- 제대로 추적 관리가 어려울 정도로 복잡한 상태라면 다른 상태 관리 라이브러리를 도입하는 게 맞지 않나 싶음
- action에 대한 컨벤션을 맞출 때 드는 비용도 고려할 필요가 있을 듯
useContext
- prop drilling 문제 해결
- context를 사용한 컴포넌트는 재사용이 힘들어지므로 이 문제 해결을 위한 다른 해결책인 '컴포넌트 합성'도 고려해볼 것
- 리액트 컴포넌트 트리 안에서 전역 데이터를 공유하고 싶을 때
- 다양한 레벨에 네스팅된 '많은' 컴포넌트들에게 한번에 데이터를 넘길 때
useRef
- 컴포넌트의 시각적 결과물에는 영향을 주지 않는 요소들을 기억할 때
- DOM을 다룰 때
- 컴포넌트가 리렌더링되어도 기억하고 싶은 내용이 있을 때
useImperativeHandle
forwardRef
랑 한 세트- 부모 컴포넌트에게 특정 기능들을 열어줄 수 있음
- props 만으로는 처리하기 어려운 일에만 사용할 것을 권장
useEffect
- 컴포넌트와 리액트 외부 시스템의 연결(동기화)
- 리액트 측에서 굉장히 많이 강조하는 사항
- 리액트 외부 시스템과 연결할 때
- data fetching
- 서버 렌더링 결과물과 클라이언트 렌더링 결과물이 달라서 오류가 생길 때
useLayoutEffect
- 이펙트 로직이 화면을 block해야만 할 때
- 일반적인 이펙트는 화면이 그려진 후에 실행되지만 가끔 그 전에 하고 싶은 경우가 있음
- browser repaint 전에 이펙트를 실행하고 싶을 때
- repaint 전에 화면 레이아웃을 계산해야 할 때
useInsertionEffect
- layout effect 실행 전에 DOM에 element를 넣고 싶을 때
- 실행 순서: insertion effect cleanup → insertion effect → layout effect cleanup → layout effect → effect cleanup → effect
- 레이아웃을 읽기 전에 스타일링을 진행해야 할 때
- 어떤 layout effect에서는 스타일을 바꾸려 하고, 또 어떤 layout effect에서는 client width를 읽을 때 순서가 꼬일 수 있음. 이 떄 insertion effect로 스타일을 먼저 바꿔서 해결하는 방식
- css-in-js 라이브러리를 구현할 때
experimental_useEffectEvent
- 특정 reactive value를 non-reactive logic으로 추출해서 exhaustive deps 문제를 해결하고 싶을 때
- 특정 reactive value A에만 이펙트가 반응했으면 좋겠는데, 해당 이펙트 안에서 reactive value B를 같이 참조해야 해서 둘 다 deps에 들어가는 경우
useMemo
- 특정 함수의 실행 결과를 기억하고 싶을 때
- 매 리렌더링마다 비싼 계산을 하고 싶지 않을 때
- props로 전달할 값을 항상 같게 유지해서 자식 컴포넌트의 리렌더링을 막고 싶을 때
- 자식 컴포넌트의 렌더링 결과물 자체를 기억하고 싶을 땐
memo
를 고려
- 자식 컴포넌트의 렌더링 결과물 자체를 기억하고 싶을 땐
- 특정 함수 자체를 기억하고 싶을 때
- 이 때는
useCallback
을 사용해도 됨
- 이 때는
- 메모이제이션을 하지 않아서 성능 저하가 느껴질 때
useCallback
- 특정 함수의 정의 자체를 기억하고 싶을 때
- 나머지는
useMemo
와 같음
useTransition
- 실제 화면이 바뀌어야 하는 건 맞지만, 그 화면을 그리는 게 오래 걸릴 때
- blocking update와 non-blocking update를 구분해야 할 때
- 성능이 좋지 않은 기기에서도 ui를 그리는 동안 다른 요소들의 상호작용성을 지키고 싶을 때
useDeferredValue
- ui의 특정 부분 업데이트를 미루고 싶을 때
- 상태값에 디바운싱을 적용하고 싶을 때
- '렌더링 측면에서' 최적화가 필요할 때
- 렌더링 도중이 아닌 일들(네트워크 요청 등)은 디바운싱이나 스로틀링이 유효
- 디바운싱/스로틀링과의 차이:
useDeferredValue
는 지연시간이 미정이라 기기가 좋으면 빨리 끝나고, 느리면 그만큼 더 기다림
- 새로운 내용이 렌더링되는 도중에도 구 렌더링 결과물을 보여주고 싶을 때
- 특정 컴포넌트의 리렌더링이 오래 걸릴 때 해당 컴포넌트에게 prop으로 넘겨주는 값을 defer 하고, 해당 컴포넌트를
memo
해서 그 컴포넌트가 다른 컴포넌트의 렌더링까지 방해하는 현상을 해결 가능
use
- promise나 context의 값을 읽고 싶을 때
- promise가 pending 또는 reject 되었을 때 fallback ui를 보여주고 싶을 때
useContext
에서 null 처리를 위한 공수를 줄이고 싶은 경우- 특정 조건일 경우에만 context를 읽고 싶을 때
use
는 조건문, 반복문 안에서 사용 가능
- 서버 컴포넌트에서 fetch한 promise를 서버에서 await으로 기다릴 경우 렌더링이 block되는데, 이게 아니라 promise 자체를 자식에게 props로 넘겨서 그 자식 컴포넌트에서 promise를 해체하는 방식으로 blocking 문제 해결 가능
useDebugValue
- React DevTools에서 커스텀 훅에게 특정 라벨을 붙이고 싶은 경우
- 라이브러리처럼 많은 곳에서 재사용되는 커스텀 훅인데 내부 자료구조가 살펴보기 어렵고 복잡할 때
useId
- unique id 값이 필요할 때
- 재사용되는 컴포넌트에서 각 사용처마다 다른 id값이 필요하므로 그 때 사용
- 접근성을 위해 특정 html element 들을 연결해줄 때
- 여러 요소들의 id에 shared prefix를 넣고 싶을 때
useSyncExternalStore
- 외부 저장소를 구독
- 리액트 외적으로 상태를 관리하는 서드 파티 라이브러리와 연동
- mutable value가 있는 브라우저 api와의 연동
- 리액트 외부 코드를 리액트 상태처럼 사용하고 싶을 때
- 외부 저장소의 값 변화가 리렌더링을 유발하게끔 조작하고 싶을 때
useOptimistic
- 낙관적 업데이트를 하고 싶을 때
- async action이 pending 상태일 때 이걸 대체할 ui(상태)를 보여주고 싶을 경우
'리액트 공식 문서 읽기' 카테고리의 다른 글
32화: useSyncExternalStore (0) | 2023.07.24 |
---|---|
31화: useState (0) | 2023.07.16 |
30화: Reusing Logic with Custom Hooks (2) | 2023.06.22 |
29화: Removing Effect Dependencies (0) | 2023.06.22 |
28화: Separating Events from Effects (0) | 2023.06.22 |