useCallback은 2개의 argument(callback function, 의존성배열)를 갖는다.
의존성 배열의 값이 바뀌었을 때 memoized callback 함수를 리턴한다.
const memoizedCallback=useCallback( () =>{something(a,b);},[a,b]);
🐹Memoization은 무엇인가?
memoized,momoization은 최적화 기술로 만약 함수의 input이 같으면 re-computing대신에 cached result를 리턴하여 값비싼 함수 호출 속도를 높인다.
🐶그럼 도대체 언제 useCallback을 사용해야 하는가?
예를 들어보자. Todo,Number,Counter 3개의 컴포넌트가 있다.
Todo 컴포넌트
는 props로 전달된 아이템들이 있고 number
컴포넌트는 number의 현재 값을 보여준다. counter 컴포넌트
는 number의 값을 변경해주는 2개의 버튼을 가지고 있다.
코드를 봐보자!
function App() {
console.log("render app");
const [items,setItems]=useState([
"1.Some todo",
"2.Some todo",
"3.Some todo"
]);
const [number,setNumber]=useState(0);
const add=()=>{
setItems(()=>[...items,"New todo"]);
};
const increase=()=>{
setNumber(number+1);
};
const decrease=()=>{
setNumber(number-1);
};
return(
<div>
<Todo items={items} add={add} />
<Number number={number} />
<Counter incr={increase} decr={decrease} />
</div>
);
}
여기서 문제는 Todo component가 업데이트될 때 불필요하게 Counter,Number 컴포넌트도 렌더 된다는 것이다. 아이템이 변경된 것도 아닌데 불필요하게 리렌더하게된다면 퍼포먼스 이슈를 줄 것이다.
1. React.memo를 사용하기
React.memo()는 memoized 컴포넌트를 렌더하고 불필요한 리렌더링을 스킵하게 한다. 즉, props에 변화를 감지할 경우만 re-render하게 된다
export default React.memo(Component);
Todo,Number,Counter컴포넌트를 React.memo로 감싸보자.
음.. 그런데 여전히 불필요한 렌더링이 일어난다.
return (
<div>
<Todo items={items} add={add} />
<Number number={number} />
<Counter incr={increase} decr={decrease} />
</div>
);
app 컴포넌트에 add,increase,decrease가 ,Counter에 props로 전달된다.
리액트에서는 컴포넌트가 리렌더할 때마다, 함수의 새로운 인스턴스가 생긴다. 따라서 해당 reference는 메모리에서 다른 함수를 가르킨다. 따라서 referential equality에 따라 리렌더링 하기 전의 함수와 리렌더링한 후의 함수는 다른 것이다!
다시 정리해보면, 'Add Todo' 버튼을 클릭하면
1. App컴포넌트가 리렌더링된다.
2. items 배열과 add,increase,decrease의 reference가 업데이트된다.
3. React.memo는 이런 변경사항들을 설명하고 컴포넌트를 리렌더링한다.
반면에, Number는 Add Todo버튼을 클릭했을 때 리렌더링하지 않는다. number prop에는 변화가 없기 때문이다.
2. useCallback을 사용해보자
위 예제같은 문제를 해결하기 위해 useCallback을 사용해보자.
useCallback을 사용하면 불필요한 리렌더링을 막을 수 있다. dependency에 변경이 없을 경우 같은 callback function reference를 리턴하도록 하기 때문이다.
const add=()=>{
setItems(()=>[...items,"New todo"]);
const add =useCallback(()=>{
setItems(()=>[...items,"New todo"]);
},[items])
const increase = useCallback(() => {
setNumber(number + 1);
}, [number]);
const decrease = useCallback(() => {
setNumber(number - 1);
}, [number]);
이제 add function은 items가 변경되었을 때만 업데이트될 것이다. increase,number는 number가 변경되었을 때만 업데이트 될 것이다.
참고: https://lo-victoria.com/a-look-at-react-hooks-usecallback