Pandaman Blog

[React.js] React Hook 에서 이전 props 또는 state를 얻는 방법 본문

Front end/React

[React.js] React Hook 에서 이전 props 또는 state를 얻는 방법

oyg0420 2021. 5. 30. 21:17

클래스형 컴포넌트를 함수형 컴포넌트로 변경하는 작업을 진행하면서 이전 상태를 알아야 하는 경우가 발생했다. react 공식 문서에서 확인해보면 아래와 같은 예제를 통해 알려주고 있다.

function Counter() {
  const [count, setCount] = useState(0);

  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  });

  const prevCount = prevCountRef.current;

  return <h1>Now: {count}, before: {prevCount}</h1>;
}

해당 코드를 다시 Custom Hook으로 변경 가능하다.

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return <h1>Now: {count}, before: {prevCount}</h1>;
}

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

이렇게 이전 상태를 저장할 수 있다고 한다. 여러분들은 해당 코드를 보고 usePrevious 동작 방식에 대해 잘 설명할 수 있는가? 개발을 해놓고 github PR(pull request)를 하기 위해 해당 코드에 설명을 하려고 하니 명확하게 설명이 되지 않았다.
두 가지 이유 때문에 설명이 잘 되지 않았다. 첫 번째로 useRef hook에 대한 이해, 두 번째로 useEffect hook에 대한 이해이다.

useRef

useRef는 ref 객체를 반환하는데, 반환된 ref 객체는 컴포넌트의 전 생애주기를 통해 유지된다. 보통 ref 객체를 사용하는 이유는 DOM(Document Object Model)에 접근하여 사용하기 위함이다. 하지만 어떠한 가변 값을 저장할 때도 매우 유용하게 사용할 수 있다.
useRef()의 인자는 ref 객체의 초기값이다. 해당 초기값은 ref의 current 속성에 할당되며, 우리는 ref.current를 통해 해당 값에 접근할 수 있다.

예시

const ref = useRef(123);
ref // { current: 123 }

그렇다면 useState를 통해 상태를 관리하는 것과 useRef를 통해 값을 관리하는 것 과의 차이점은 무엇일까? useState를 통해서 상태를 업데이트를 하게 된다면 해당 컴포넌트가 re-rendering을 하게 된다는 것을 알고 있다. 반면에 ref 객체는 업데이트가 돼도 re-rendering 하지 않는다.

useEffect

부끄럽지만 React Hook를 사용하면서 useEffect가 denpendency에 의해 실행된다는 점은 알고 있었으나, rendering 이후 실행된다는 사실은 모르고 있었다..

 

위 내용에 대해 이해했으니, 코드를 순서대로 확인해보자.

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return <h1>Now: {count}, before: {prevCount}</h1>;
}

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

Counter 앱은 아래와 같은 순서로 실행된다.

  1. count 상태는 0이다.
  2. count는 usePrevious의 인자가 된다. 현재 usePrevious의 ref.current는 undefined이다. value는 useEffect hook에서 ref.current에 할당되는 듯 하지만 아직 useEffect hook은 실행되지 않는다. (rendering 이후 실행) 따라서, ref.current는 undefined를 return 한다.
  3. prevCount는 undefined가 된다.
  4. 화면에 현재 카운트와 이전 카운트가 노출된다.(rendering end)
  5. usePrevious의 useEffect hook이 실행되어 ref.current에 value = 0를 할당한다. 따라서 ref.current = 0 이 된다.

다음 setCount에 의해 count값이 1로 업데이트되었다고 가정하면 아래와 같이 실행된다.

  1. count가 0에서 1로 업데이트되는 순간 re-rendering이 된다.
  2. count는 usePrevious의 인자가 된다. 현재 usePrevious의 ref.current는 0이다. ref.current는 0을 return 한다.
  3. prevCount는 0이 된다.
  4. 화면에 현재 카운트 1과 이전 카운트 0이 노출된다.(re-rendering end)
  5. usePrevious의 useEffect hook이 실행되어 ref.currennt에 value = 1을 할당한다. 따라서 ref.current = 1 이 된다.
  6. 위와 같이 count 상태가 업데이트될 때마다 위와 같은 순서로 실행된다.

 

이번에 공부를 하면서 useRef, useEffect 두 hook에 대해 더 깊이 이해할 수 있었다.
ref 객체의 유지와 useEffect가 rendering 이후 실행된다는 점이 이번 공부를 하면서 얻은 가장 큰 소득이다.

Comments