Modern React Deep Dive: 리액트 훅 깊게 살펴보기 - 2

지난 글에 이어 여러 훅을 알아보자.

useMemo

const memoizedValue = useMemo(() => expensiveComputation(a, b), [a, b]);

useMemo는 첫 인자로 들어오는 함수의 연산 결과(비용이 큰)를 메모이제이션 해 두고 두 번째 인자인 의존성 배열의 값이 변경되지 않았으면 그 메모이제이션 된 값을 반환해주는 훅이다.

컴포넌트도 값이기 때문에 이를 통해 컴포넌트 또한 메모이제이션 할 수 있지만, React.memo를 사용하는 것이 더 현명하다.

useCallback

const toggle = useCallback(() => {
  setIsOpened(!isOpened);
}, [isOpened]);

useMemo가 콜백의 반환값을 기억한다면 useCallback은 콜백 자체를 기억한다.

const toggle = () => {
  setIsOpened(!isOpened);
};

함수를 위와 같이 선언하게 되면 함수 컴포넌트가 리렌더링될 지 판단할 때 함수 컴포넌트가 재실행 되면서 함수 또한 새로 생성되고 할당되기 때문에 리렌더링이 일어난다.
useCallback의 목적은 그것을 방지하기 위해서이다.

useRef

useRef는 useState와 비슷하지만, 그 값이 변하더라도 렌더링을 발생시키지 않는 훅이다.

일반적으로 DOM에 접근하고 싶을 때나, 함수 컴포넌트에서 클래스 컴포넌트의 인스턴스 변수 처럼 값을 유지하고 싶을 때 사용한다.

useRef의 활용: usePrevious

function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, value); // value가 변경되면 그 값을 ref에 넣어둔다.

  return ref.current;
}

이 커스텀 훅은 useState의 이전 값을 저장하고 싶을 때 유용한 커스텀 훅이다.

useContext

useContext는 리액트 Context에 함수 컴포넌트에서도 접근할 수 있게 해주는 훅이다.

useContext를 사용할 때의 주의점

  1. Context API는 상태 관리를 위한 API가 아니라 상태를 주입해주는 API다.
    받은 props 값을 하위로 전달해 줄 뿐이다.

  2. Context를 사용하면 함수 컴포넌트의 재활용이 어려워진다.
    이를 방지하기 위해 useContext를 사용하는 컴포넌트를 최대한 작게 하거나 재사용되지 않을 만한 컴포넌트에서만 사용해야 한다.

  3. Context의 Provider 아래에 있는 모든 컴포넌트가 리렌더링된다.
    React.memo를 사용하여 리렌더링을 방지해줘야 한다.

useReducer

useReducer는 useState의 심화 버전으로, 더 복잡한 상태값을 미리 정의해 놓은 시나리오에 따라 관리할 수 있다.

type State = {
  count: number,
};

type Action = { type: "up" | "down" | "reset", payload?: State };

const initialState: State = { count: 0 };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case "up":
      return { count: state.count + 1 };
    case "down":
      return { count: state.count - 1 > 0 ? state.count - 1 : 0 };
    case "reset":
      return initialState;
    default:
      throw new Error(`Unexpected action type: ${action.type}`);
  }
}

복잡한 형태의 state를 사전에 정의된 reducer에 따라서만 수정할 수 있게 제한하는 것이 목적이다.
state를 사용하는 로직과 이를 관리하는 비즈니스 로직을 분리할 수 있어 state를 관리하기가 한결 쉬워진다.

useLayoutEffect

useEffect와 형태가 동일하나, 모든 DOM의 변경 후에 동기적으로 부수 효과 함수를 실행한다.
여기서 말하는 DOM의 변경이란 리액트의 렌더 단계가 마쳤음을 말하는 것으로, 실제 브라우저에 반영되는 것과는 다르다.

실행 순서

  1. 리액트가 DOM을 업데이트

  2. useLayoutEffect를 실행

  3. 브라우저에 변경 사항을 반영

  4. useEffect 실행

DOM은 계산됐지만 이것이 화면에 반영되기 전에 하고 싶은 작업이 있을 때 사용해야 한다. (ex: 스크롤 위치, 애니메이션 등)

훅의 규칙

  1. 컴포넌트 최상위에서만 훅을 호출해야 한다. 또한 컴포넌트가 렌더링 될 때 분기, 반복 없이 동일한 순서로 훅이 호출되는 것을 보장해야 한다.

  2. 훅을 호출할 수 있는 것은 함수 컴포넌트 혹은 커스텀 훅 뿐이다.