useCallback()
const memoizedCallback = useCallback(() => {doSomething(a, b)}, [a,b]);
메모이제이션된 콜백을 반환한다.
인라인 콜백과 그것의 의존성 값의 배열을 전달한다. useCallback은 콜백의 메모이제이션된 버전을 반환한다. 그 메모이제이션된 버전은 콜백의 의존성이 변경될 때에만 변경이된다. 불필요한 렌더링을 방지하기 위해(shouldComponentUpdate를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달 할 때 유용하다.
💡useCallback(fn, deps) 는 useMemo(() => fn, deps)와 같다.
- 메모이제이션을 다시 계산할 필요가 없도록 값을 캐싱하는 것이다.
- 이를 통해 리소스 집약적인 기능을 분리하여 모든 렌더링에서 자동으로 실행되지 않도록 할 수 있다.
- useCallback 종속성 중 하나가 업데이트될 때만 실행된다.
지켜야할 규칙
useMemo와 useCallback을 사용하지 말아야할 경우
- host 컴포넌트(div, span, a, img... 등)에 전달하는 모든 항목에 대해 쓰지 말아야한다. 리액트는 여기에 함수 참조가 변경되었는지 신경쓰지 않는다.(ref는 여기서 제외 된다.)
- leaf 컴포넌트에는 쓰지 말아야한다.
- useCallback, useMemo의 의존성 배열에 완전히 새로운 객체와 배열을 절달해서는 안된다. 이는 항상 의존성이 같지 않다는 결과를 의미하며, 메모이제이션을 하는데 소용이 없다. useEffect, useCallback, useMemo의 종속성은 참조 동일성을 확인한다.
// 사용 X
const x = ['hello'];
const cb = useCallback(() => {}, [props, x])
const [a, ...rest] = someArray;
const cb = useCallback(() = {}, [rest])
- 전달하려는 항목이 새로운 참조여도 상관없다면, 사용하지 말아야 한다. 매번 새로운 참조여도 상관없는데, 새로운 참조라면 메모이제이션하는 것이 의미가 없다.
host 컴포넌트 : 호스트 환경(브라우저 또는 모바일)에 속하는 플랫폼 컴포넌트를 의미한다. DOM 호스트의 경우, div, img와 같은 요소가 될 수 있다.
leaf 컴포넌트 : DOM에서 다른 컴포넌트를 렌더링하지 않는 컴포넌트(html 태그만 렌더링하는 컴포넌트)
useMemo와 useCallback 을 사용해야 하는 경우
- 하위트리에 많은 Consumer가 있는 값을 Context Provider에 전달해야 하는 경우 useMemo를 사용하는 것이 좋다. "<ProductContext.Provider value = {{id, name}}>"의 경우, 어떤 이유로든 해당 컴포넌트가 리렌더링 된다면 id, name이 동일하더라도 매번 새로운 참조를 만들어 죄다 리렌더링 될 것이다.
- 계산 비용이 많이 들고, 사용자의 입력 값이 map과 filter을 사용했을 때와 같이 렌더링 이후로도 참조적으로 동일할 가능성이 높은경우, useMemo를 사용하는 것이 좋다.
- ref 함수를 부수작용과 함께 전달하거나, mergeRef-style과 같이 wrapper 함수 ref를 만들 때 useMemo를 쓴다. ref 함수가 변경이 있을 때마다, 리액트는 과거 값을 null로 호출하고 새로운 함수를 호출한다. 이 경우 ref 함수의 이벤트 리스너를 붙이거나 제거하는 등의 불필요한 작업이 일어날 수 있다. 예를들어, useIntersectionObserver가 반환하는 ref의 경우 ref 콜백 내부에서 observer의 연결이 끊기거나 연결되는 등의 동작이 일어날 수 있다.
- 자식 컴포넌트에서 useEffect가 반복적으로 트리거되는 것을 막고 싶을 때 사용한다.
- 매우 큰 리액트 트리 구조 내부에서, 부모가 리렌더링 되었을 때 이에 다른 렌더링 전파를 막고 싶을 때 사용. 자식 컴포넌트가 React.memo, ReactPureComponent일 경우, 메모이제이션된 props를 사용하게 되면 딱 필요한 부분만 리렌더링 될 것이다.
React DevTools Profilter를 사용하면 컴포넌트의 리렌더링 속도가 느린 경우, 상태 변경이 일어났을 때 얼마나 렌더링 시간이 걸렸는지 조사할 수 있다. 이렇게 하면 거대한 계단식 리렌더링을 방지하기 위해 React.memo를 사용할 위치를 찾을 수 있고, 필요한 경우 useCallback, useMemo를 사용하여 상태변경을 더 효율적으로 만들 수 있다.