const memoizedCallback = useCallback(()=>{
// 함수본문
},
[/* 의존성 배열*/]
)
기능 : 메모이제이션을 통한 불필요한 렌더링 방지
원리 : 이전 렌더링에서 생성된 콜백함수를 기억하고, 다음 렌더링에서 새로운 함수를 생성하는 대신
기억된 함수를 반환. 함수 내부에서 참조되는 상태가 props가 변경되지 않았을 경우, 기억된 함수를 재사용.
첫번째 인자로 콜백 함수를 받고, 두번째 인자로 의존성 배열을 받음.
의존성 배열에 포함된 값이 변경되었을 때만 새로운 함수를 생성. 생략시 동일한 인스턴스 반환.
const ParentComponent = () => {
const onClickHandler = useCallback(()=>{
console.log('Memoized Callback')
},[])
return (
<>
<ChildComponent onClick={onClickHandler}/>
</>
)
}
자식컴포넌트의 onClick 이벤트의 props 로 onClickHandler 함수를 전달하고 있는데
메모이제이션을 통해 리렌더링될 때마다 새로운 함수를 생성하는 것을 방지할 수 있다.
const Counter = () => {
const [count, setCount] = useState(0);
// const onClickHandler = () => {
// setCount(count + 1);
// };
const onClickHandler = useCallback(()=>{
setCount(count + 1)
},[count])
return (
<>
<h1>{count}</h1>
<button onClick={onClickHandler}>+1</button>
</>
)
}
새로운 함수가 생성되면 해당 함수가 전달된 props도 새로운 값으로 간주되므로,
부모 컴포넌트나 자식 컴포넌트가 리렌더링 될 가능성이 있다.
다른 관점에서 보면, onClickHandler 함수를 참조하는 자식 컴포넌트가 많다고 가정했을 때,
각 자식 컴포넌트가 렌더링 될 때마다 새로운 함수를 생성하게 되면서 함수의 생성 비용이 누적된다.
이러한 관점에서 볼 때, onClickHander 함수가 자주 호출될 가능성이 높으며 바뀌지 않을 경우
useCallback 으로 불필요한 렌더링을 방지할 수 있다. 또한 다른 자식 컴포넌트가 참조하여 사용하더라도
새로운 함수를 생성하지 않기 때문에 함수 생성 비용을 절약할 수 있다.
const MemoizedComponent = () => {
const [value, setValue] = useState('');
// 계산 비용이 높은 함수
const heavyCalculation = (arg1, arg2) => {
// 계산에 매우 오래걸리는 로직
};
// 캐싱된 함수
const memoizedHeavyCalculation = useCallback(
() => {
return heavyCalculation(value, 100);
},
[value]
);
return (
<div>
<p>Result: {memoizedHeavyCalculation()}</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
</div>
);
}
예를들어, 데이터베이스의 쿼리를 실행하거나 반복문을 만 번 이상 실행하는 계산 비용이 높은 함수를
매번 호출하면 성능의 저하가 불가피하다.
이때 useCallback 을 사용하여 memoizedHeavyCalculation 함수를 생성하고,
heavyCalculation 함수를 호출하여 결과만 반환한다. 그런데 의존성 배열에 value 가 있으므로
value의 값이 변경될 때만 heavyCalculation 함수의 계산을 다시 하게 되므로 성능의 저하를 막을 수 있다.
useCallback 을 사용하면 계산비용을 아끼고 성능적 이점을 가질 수 있다.