[TIL] React.useCallback 이란?

gun·2021년 4월 6일
8

TIL

목록 보기
17/19

useCallback

useCallback은 특정 함수를 새로 만들지 않고 재사용하고 싶을 때 사용합니다.
React 공식 문서에서는 useCallback을 다음과 같이 말하고 있습니다.

메모제이션된 함수를 반한하는 하는 함수입니다.

인라인 콜백과 그것의 의존성 값의 배열을 전달하세요. useCallback은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.

  • React Hooks useCallback 문서

왜 useCallback을 사용해야 할까요?

현재 하위 컴포넌트에 전달하는 콜백 함수가 inline 함수로 사용되거나, 컴포넌트 내에서 함수를 생성하고 있다면 새로운 함수가 만들어지게 됩니다. 예를 들어보자면, Counter안에 increament함수들은 컴포넌트가 리렌더링 될 때 마다 새로 만들어지게 됩니다.

함수를 재 선언 하는것은 CPU, 큰 메모리도 차지하지 않지만, 한번 만든 함수를 재 사용하고, 필요할 때만 재 생성 하는것은 중요합니다. 아래 예제에서 useCallback과 React.memo를 함께 사용한다면 컴포넌트를 최적화 할수 있습니다.

import React, { useState } from "react";

const CounterButton = function ({ onClicks, count }) {
  console.log("카운터 버튼 렌더링");
  return <button onClick={onClicks}>{count.num}</button>;
};

export default function Counter() {
  const [count1, setCount1] = useState({ num: 0 });

  const increament1 = () => {
    setCount1({ num: count1.num + 1 });
  };

  const [count2, setCount2] = useState({ num: 0 });

  const increament2 = () => {
    setCount2({ num: count2.num + 1 });
  };

  return (
    <div className="App">
      <div>{count1.num}</div>
      <div>{count2.num}</div>
      <CounterButton onClicks={increament1} count={count1} />
      <CounterButton onClicks={increament2} count={count2} />
    </div>
  );
}

버튼을 클릭하면 CounterButton 컴포넌트가 2번 렌더링 되게 됩니다.
왜 2번 렌더링 되는것일까요?
원인을 알아보기 위해선 React의 렌더링 과정을 살펴봐야 합니다.
React가 리렌더링을 하는 조건

  • props가 변경되었을 때
  • state가 변경되었을 때
  • 부모 컴포넌트가 렌더링되었을 때
  • forceUpdate() 를 실행하였을 때

다음 예제에서는 props, state,부모 컴포넌트의 변화가 있었기에 React가 리렌더링 되게 됩니다.

  1. 첫 렌더링이 되고 increament함수와 count state가 생성되어 렌더링 됩니다.
  2. 버튼을 클릭하게 되면 increament함수가 작동하게 되고 setState로 인해 state가 변경됩니다.
  3. state가 변경됐으니, 부모 컴포넌트는 리 렌더링이 되게 되고, 변경된 props를 내려주게 됩니다.
  4. 자식컴포넌트는 props를 받아 다시 뿌려주게 됩니다.

위 과정을 통해 CounterButton이 두번 렌더링 되게 되는것 입니다.

useCallback 사용

  //수정 전
  import {useCallback} from 'react';
  const increament1 = () => {
    setCount1({ num: count1.num + 1 });
  };
  //수정 후
  const increament1 = useCallback(() => {
    setCount1({ num: count1.num + 1 });
  },[count1]);
	

useCallback의 첫번째 인자로는 인라인 콜백과 의존성 값의 배열을 받게 됩니다.

useCallback(fn, deps)

의존성 배열인 deps에 변경을 감지해야할 값을 넣어주게 되면 count1이 변경될 때마다 콜백 함수를 새로 생성하게 됩니다.

useCallback의 첫번째 인자로는 여태껏 최적화를 위해 useCallback을 사용한다고 했는데 눈에 뛰는 큰 변화가 없습니다.
위 React에서 발췌해온 글을 다시 보시면 다음과 같은 문구가 있습니다.

이것은, 불필요한 렌더링을 방지하기 위해 (예로 shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.

참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용하다?
다시말해 최적화된 자식 컴포넌트에 props로 콜백 함수를 내려줄 때를 말하는것 같습니다.
다음 포스팅에서 useMemo를 사용해 자식 컴포넌트를 최적화 시켜, 컴포넌트 최적화를 진행해 보겠습니다.

0개의 댓글