[React] useCallback 사용 이유 및 방법

Featured image

공식 문서에 정의된 내용에 의하면 useCallback 은 메모리제이션 된 콜백을 반환한다. 평소 자주 사용하는 useCallback 를 정확하게 왜 써야하는지에 대해서 알아보자.


배열, 객체와 비슷하게 함수는 값이 아닌 참조로 비교 된다.

const functionOne = function() {
  return 5;
};
const functionTwo = function() {
  return 5;
};
console.log(functionOne === functionTwo); // false

functionOne 과 functionTwo 의 함수는 동일하게 보이는 함수이지만 두 함수를 비교했을 경우 false 가 나오게 된다.

이처럼 Components 내에서 함수를 정의하게 된다면, 모든 단일 render 에서 매번 동일하지만 고유한 함수를 생성하게 될 것 이다.

매번 고유한 함수를 생성하게 된다면, 이 props 함수를 전달 받는 Component에서는 props 가 변경됐다고 판단하여 매번 리렌더링이 발생한다.

아래의 Count 예제를 살펴보자.

// App.jsx
import Reset from './Reset';

function App() {
  const [count, setCount] = React.useState(0);

  function handleReset() {
    setCount(0);
  }

  return (
    <>
      Count: {count}
      <button
        onClick={() => {
          setCount(count + 1)
        }}
      >
        Click me!
      </button>
      <Reset handleClick={handleReset} />
    </>
  );
}

export default App;
//Reset.jsx
function Reset({ handleClick }) {
  console.log('Render Reset');
  
  return (
    <button
      onClick={handleClick}
    >
      Reset Count!
    </button>
  );
}

export default React.memo(Reset);

Reset 컴포넌트는 React.memo 덕분에 Pure Component로 구성되어 있다. count에 의존되지 않는다고 생각 되지만 실제로는 count 가 변경될 때 마다 리렌더링 된다.

여기서 문제는 React.memo 의 기능을 뚫고 매번 고유한 handleReset 함수가 생성 된다. 그렇기 때문에 props 가 변경됐다고 판단되어 매번 리렌더링이 발생한다.

이때 useCallback 을 사용하여 불필요한 리렌더링을 막을 수 있다.

const handleReset = React.useCallback(() => {
  setCount(0);
}, []);

위의 코드로 처리한다면, 숫자가 변경될 때 마다 리렌더링은 더 이상 발생하지 않게 된다.

useCallback 은 두 개의 인자를 넘겨준다.

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

첫 번째 인자로 함수를 전달하며, 해당 함수를 메모리제이션 하여 render 간에 전달한다.

두 번째 인자로는 의존성 값을 전달한다. 두 번째 인자로 넘겨준 의존성이 변경 되었을 경우만 리렌더링이 될 것 이다. (위의 count 예제에서는 의존하는 값이 없기 때문에 빈 배열을 넘겨줬다.)


Referenxce