컴포넌트가 리렌더링되는 과정에서 컴포넌트 내부의 함수들이 지속적으로 호출되고 연산된다.
useMemo(콜백함수, 의존자배열)은 의존자 배열이 그대로인 경우 콜백함수를 호출하지 않도 memoization된 값을 사용한다.
컴포넌트가 리렌더링 되는 과정에서 컴포넌트 내부의 함수 선언문들이 실행되며 새로운 함수가 생성된다.
이로 인한 성능 저하를 명확히 수치화할 수는 없으나, 예기치 못한 성능 저하를 막기 위해 관습적으로 useCallback을 이용하여 함수를 메모이제이션 한다.
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
// useCallback의 의존자 배열을 비워두면 최초 마운트시 생성된 함수를 계속 이용한다.
const onChangeHandler = useCallback((e) => {
setNumber(e.target.value);
}, []);
// 그 외의 경우 콜백 함수 내에서 사용되는 변수들이 변경될 때에 새로운 함수를 생성하도록 의존자 배열을 선언한다.
const onSubmitHandler = useCallback(() => {
const newList = list.concat(parseInt(number));
setList(newList);
setNumber("");
}, [number, list]);
// useMemo를 사용하여 숫자 리스트가 그대로인 경우 새로운 평균값을 구하지 않도록 최적화 하였다.
const avg = useMemo(() => {
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
}, [list]);
React.memo() 함수는 해당 컴포넌트의 props가 변경되는 경우에만 함수형 컴포넌트를 호출하여 리렌더링하는 함수이다.
해당 예시를 보자.
import React, { useState, useCallback } from "react";
function Light({ room, on, toggle }) {
console.log({ room, on });
return (
<button onClick={toggle}>
{room} {on ? "💡" : "⬛"}
</button>
);
}
Light = React.memo(Light);
Light 컴포넌트를 React.memo() 함수로 감쌌다.
export default function SmartHome() {
const [masterOn, setMasterOn] = useState(false);
const [kitchenOn, setKitchenOn] = useState(false);
const [bathOn, setBathOn] = useState(false);
const toggleMaster = () => setMasterOn(!masterOn);
const toggleKitchen = () => setKitchenOn(!kitchenOn);
const toggleBath = () => setBathOn(!bathOn);
return (
<div className="App">
<Light room="침실" on={masterOn} toggle={toggleMaster} />
<Light room="주방" on={kitchenOn} toggle={toggleKitchen} />
<Light room="욕실" on={bathOn} toggle={toggleBath} />
</div>
);
}
이후 위와 같이 침실, 주방, 욕실로 나누어 on에 각각의 state를 props로 넘겨주었다.
헌데 위의 코드는 리렌더링마다 3번의 Light 컴포넌트가 호출된다. 3개의 toggle 함수가 모두 재생성되고, 이로 인해 Props의 변경이 탐지되기 때문이다. useCallback을 이용하여 toggle함수의 재생성을 막을 수 있다.
export default function SmartHome() {
const [masterOn, setMasterOn] = useState(false);
const [kitchenOn, setKitchenOn] = useState(false);
const [bathOn, setBathOn] = useState(false);
const toggleMaster = useCallback(() => setMasterOn(!masterOn), [masterOn]);
const toggleKitchen = useCallback(
() => setKitchenOn(!kitchenOn),
[kitchenOn]
);
const toggleBath = useCallback(() => setBathOn(!bathOn), [bathOn]);
return (
<div className="App">
<Light room="침실" on={masterOn} toggle={toggleMaster} />
<Light room="주방" on={kitchenOn} toggle={toggleKitchen} />
<Light room="욕실" on={bathOn} toggle={toggleBath} />
</div>
);
}