
개발하다보니 잊어버리는 부분이 있어 글로 남깁니다. useCallback과 React.Memo을 통한 렌더링 최적화의 글을 읽고 작성된 글입니다.
TL;DR
useCallback은 deps에 의해 함수를 재사용할지 말지 결정하고, 함수 재사용 뿐만 아니라 props를 통해 컴포넌트 재사용까지 가능합니다.
useCallback의 인터페이스는 아래와 같습니다.
function useCallback <T extends (...args: any[]) => any>
(callback: T, deps: DependencyList): T;
useCallback의 두 번째 인자인 deps 배열 안의 값이 변하지 않으면, callback: T 를 새로 만들지 않고 재사용할 수 있도록 하는 hook 입니다. 사실 복잡한 계산이 필요한 함수를 재사용하는 것이 아니라면 큰 효과를 볼 수 없습니다.
하지만 함수의 재사용 측면이 아닌 컴포넌트 재사용 측면에서 보면 유용합니다. 컴포넌트의 props를 받을 때, props가 바뀌지 않고 결과가 같다면 굳이 다시 렌더링할 필요가 없습니다.(아래의 React.memo 설명)
여기까지만 하고 memo로 넘어가겠습니다. 이 섹션에서 기억해야하는 것은 useCallback은 deps에 의해 함수를 재사용할지 말지 결정하고, 함수 재사용 뿐만 아니라 컴포넌트 재사용까지 가능하다. 여기서 더 나아가기 위해 React.memo()의 설명이 더 필요할 것 같습니다.
TL;DR
React.memo은 함수형 컴포넌트를 인자로 받는데 이 컴포넌트가 받는 props의 상태가 변하면 리렌더링합니다.
memo의 인터페이스는 아래와 같습니다.
function memo<P extends object>(
Component: SFC<P>,
propsAreEqual?: (prevProps: Readonly<PropsWithChildren<P>>,
nextProps: Readonly<PropsWithChildren<P>>) => boolean
): NamedExoticComponent<P>;
컴포넌트를 인자로 받고 prevProps와 nextProps를 비교하여 다르다면 렌더링하고, 같다면 리렌더링하지 않는 hook입니다. useCallback과 구조상 차이가 있지만 맥락적으로 보면 이전과 이후를 비교하여 다르다면 다시 만듭니다. 함수를 자식 컴포넌트에 props로 보내는 경우 useCallback과 useMemo가 같이 쓰일 수 있습니다.
주의 할 점은 부모 컴포넌트의 render() 안에 ()=>{} 같은 arrow function을 인라인하게 되면 자식 컴포넌트는 props로 함수의 메모리 주소를 받아오는데 부모 컴포넌트의 리렌더링 후 props로 같은 함수 코드가 다른 메로리 값으로 들어오니 당연히 자식 컴포넌트도 리렌더링 되고 리소스를 낭비하게 됩니다.
useCallback은 함수를 재사용할 때 주로 쓰이는데 자식 컴포넌트의 props로 함수를 넘겨줄 경우 유용합니다. props로 함수를 자식 컴포넌트에게 넘겨주는 경우 재사용 가능한 함수(메모리 참조값이 같은 함수)가 props로 넘어가면 자식 컴포넌트는 React.memo를 통해 같은 값으로 인식하여 불필요한 렌더링을 줄일 수 있습니다.