useCallback
memoization 되어 있는 콜백함수 자체를 반환한다.
useMemo가 함수의 리턴 값을 기억한다면 useCallback은 함수 자체를 기억하기에 복잡한 함수가 생성까지 오래 걸리는 경우 쓰면 최적화에 도움이 된다.
useCallback은 두번째 매개변수로 의존성배열을 갖는다. 의존성 배열 내에 값을 넣고 , 그 값이 변경되는 경우에는 이전에 기억하고 있던 함수 자체와 비교하여 다른 경우에 리렌더링한다.
복잡한 함수의 리렌더링을 막는게 주 목적이므로 useCallback의 의존성 배열은 주로 빈 배열만 넣어 사용한다.(함수의 반환값이 필요하다면 useMemo를 사용하면 된다.)
부모 컴포넌트에서 자식 컴포넌트에 함수를 props로 전달할 때에는 useCallback을 반드시 사용하여 자식 컴포넌트의 리렌더링을 방지한다.
사용 예시 )
const boo = () => {return 1;}
const boo = useCallback(() => {return 1;},[])
React.memo()
부모컴포넌트에서 자식컴포넌트로 props를 내려줄 때, props가 함수라면 반드시 Callback을 사용하여 props로 내려주어야 한다고 하였다. 하지만 그것만으로는 자식컴포넌트의 리렌더링을 완벽히 막을 수 없다. 그렇기에 자식 컴포넌트에서는 React.memo를 사용하여 props가 변경이 없다면 렌더링이 되지 않게 방지하여야 한다.
React는 먼저 컴포넌트를 렌더링한 뒤 이전 렌더된 결과를 shallow copy 후 비교하여 DOM 업데이트를 결정한다. 만약 렌더 결과가 이전과 다르다면 React는 DOM을 업데이트한다.
컴포넌트가 React.memo()로 래핑 될 때 React는 컴포넌트를 렌더링하고 그 결과를 메모한다.(Memoizing) 그리고 다음 렌더링이 일어날 때 props가 같다면 React는 Memoizing된 내용을 재사용한다.
export function ChildComponent({ someProps, someFuncProps }) {
return (
<div>
<div>{someProps}</div>
<div>{somFuncProps}</div>
</div>
);
}
export const MemoChildComponent = React.memo(ChildComponent);
React.memo(ChildComponent)를 통해 새로 메모이징 된 MemoChildComponent를 반환한다. 이때 someProps와 somFuncProps가 변경되지 않는다면 다음 렌더링때 메모이징 된 내용을 그대로 사용한다.
메모이징된 결과를 재 사용하면 DOM에서 달라진 부분을 확인하지 않아 성능상의 이점을 누릴 수 있다.
React.memo() 정리
React.memo는 부모컴포넌트를 통해 받은 props가 부모컴포넌트에서 변경되지 않는다면 ChildComponent에서 리렌더링이 발생하지 않게 한다.
부모컴포넌트가 다른 props로 인해 계속 리렌더링이 발생된다 하여도 ChildComponent가 받은 props가 변경되지 않는다면 ChildComponent에서 리렌더링이 발생하지 않는다.
반대로, 렌더링 될 때마다 props가 자주 변경되는 컴포넌트에서는 memoization 기법의 이점을 얻기 힘들다.(오히려 성능이 악화될 수 있다)
useCallback을 React.memo()와 사용하는 이유
함수 객체는 일반 객체와 동일한 비교 원칙을 따른다. 함수 객체는 오직 자신에게만 동일하기에 자식에게 내려주는 함수는 새 함수는 기능이 같은 다른 함수로 생성되어 props로 내려준다.(shallow copy)
함수의 동등성이란 함정 때문에 메모이제이션을 적용할 때는 콜백을 받는 컴포넌트 관리에 주의해야 한다. 리렌더링이 진행될 때마다 부모 함수가 다른 콜백 함수의 인스턴스를 넘길 가능성이 있다. 부모 컴포넌트가 자식 컴포넌트의 콜백 함수를 정의할 때 새 함수가 임시적으로 생성된다는 점을 useCallback을 통해 방지할 수 있다.
이해를 돕기 위한 예시
부모 컴포넌트가 가진 onLogout 함수에 useCallback을 사용한다.
function ParentComponent() {
const onLogout = useCallback(() => {},[]);
...
return {
<div>
<MemoizedLogout username={username} onLogout={onLogout} />
</div>
Logout 컴포넌트는 부모 컴포넌트로부터 콜백함수 prop인 onLogout을 받고 React.memo를 사용한다.
function Logout({ username, onLogout }) {
return <div onClick={onLogout}>Logout{username} />
}
const MemoizedLogout = React.memo(Logout);
이렇게 부모 컴포넌트가 자식 컴포넌트의 콜백 함수를 정의할 때 새 함수가 임시적으로 생성된다는 점을 useCallback을 통해 방지할 수 있다.
ps. 식별자로 분리하여 사용하지 않아도 된다.
export default React.memo(ChildComponent)
https://ko.reactjs.org/docs/hooks-reference.html#useeffect
https://ui.toast.com/weekly-pick/ko_20190731