안녕하세요.
저번 포스팅에서 useMemo
에 대해 살펴보았습니다. 이번 포스팅에서 알아볼 useCallback
은 useMemo
와 구조적으로 비슷한 Hook이라고 할 수 있습니다.
이 녀석은 렌더링 성능을 최적화 해야할 때 사용하는데, 만들어 둔 함수를 재사용할 수 있다는 특징이 있습니다.
React.js 공식 문서에서 어떻게 이야기하고 있는지 먼저 살펴볼게요.
메모이제이션된 콜백을 반환합니다.
인라인 콜백과 그것의 의존성 값의 배열을 전달하세요.useCallback
은 콜백의 메모이제이션된 버전을 반환할 것입니다. 그 메모이제이션된 버전은 콜백의 의존성이 변경되었을 때에만 변경됩니다. 이것은, 불필요한 렌더링을 방지하기 위해 (예로shouldComponentUpdate
를 사용하여) 참조의 동일성에 의존적인 최적화된 자식 컴포넌트에 콜백을 전달할 때 유용합니다.
useCallback(fn, deps)
은useMemo(() => fn, deps)
와 같습니다.
그리고 이전 포스팅에서 사용한 코드를 가져와 보겠습니다.
import React, { useState } from "react";
const getAverage = (nums) => {
console.log("Calculating Average");
if (nums.length === 0) return 0;
const sum = nums.reduce((x, y) => x + y);
return sum / nums.length;
};
const Average = () => {
const [valueList, setValueList] = useState([]);
const [num, setNum] = useState("");
const onChange = (e) => {
setNum(e.target.value);
};
const onInsert = () => {
const nextValueList = valueList.concat(parseInt(num));
setValueList(nextValueList);
setNum("");
};
const memorizedValue = useMemo(() => getAverage(valueList), [valueList]);
return (
<div>
<input value={num} onChange={onChange} />
<button onClick={onInsert}>Add</button>
<ul>
{valueList.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>Average: {memorizedValue}</div>
</div>
);
};
export default Average;
여기에서 onChange
와 onInsert
라는 함수를 선언해주었는데, 이런식으로 함수를 선언하게 되면 Component가 리렌더링 될 때마다 계속 새로 만들어지는 함수를 사용해야 합니다.
컴포넌트의 렌더링이 자주 발생하거나 개수가 많아지면 최적화를 해주는 것이 중요한데 이 때문에 useCallback
을 사용할 수 있습니다.
onChange
와 onInsert
를 수정해봅시다.
import React, { useState, useMemo, useCallback } from 'react';
...
// 컴포넌트가 처음 렌더링될 때만 함수를 만들어줍니다.
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(() => {
const nextValueList = valueList.concat(parseInt(num));
setValueList(nextValueList);
setNum('');
}, [num, valueList]); // num과 valueList가 바뀔때만 함수를 만들어줍니다.
...
useCallback();
의 기본꼴을 보면,
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
이와 같이 이루어져있고, 첫번째 parameter로는 생성하고자 하는 함수를 넣고,
두번째 parameter에는 배열을 넣어줍니다.
이 때 배열은 어떤 값이 바뀌었을 때 함수를 새로 생성해야 할 지를 알려주는 것입니다.
비어 있는 배열을 넣게되면 컴포넌트가 렌더링 될 때 만든 함수를 계속적으로 사용하게 됩니다.
배열 안에 어떤 값을 넣어주게 되면 input
내용이 바뀌거나 새로운 항목이 추가될 때 새로 만들어진 함수를 사용합니다.
함수 내부에서 state의 값에 의존할 때 그 값을 반드시 두번째 parameter에 표기해주어야 합니다. 왜냐하면 위의 예시에서 onInsert
를 참고하면 기존의 배열을 조회해서 새로운 배열을 만들어주기 때문입니다.