본문 바로가기

리액트 공식 문서 읽기

33화: Built-in React Hooks

앉아있는 참새.  Unsplash 에 Manthan Gajjar 님이 올림.

들어가는 말

이 글은 리액트 공식 문서의 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(상태)를 보여주고 싶을 경우

'리액트 공식 문서 읽기' 카테고리의 다른 글