useMemo / useCallback을 알아보기에 앞서 알아야하는 것
- 함수형 컴포넌트 또한 jsx를 반환하는 함수이다.
- 컴포넌트가 렌더링 된다는 것은 그 함수(컴포넌트)가 호출되어 실행된 것이며, 함수가 실행될 때마다 내부에 선언되어 변수나 다른 함수들도 매번 다시 선언되어 사용된다.
- 컴포넌트는 자신의 state가 변경되거나, 부모에게서 받는 props가 변경될 때마다 리렌더링 되며, 하위 컴포넌트는 최적화 설정을 해주지 않으면 부모에게서 받는 props가 변경되지 않았더라도 리렌더링 되는 것이 기본이다.
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
- 메모이제이션 된 값을 반환하는 훅.
- 리렌더링이 되더라도 특정 값이 다시 연산될 필요가 없을 경우, 그 값을 메모이제이션 해뒀다가 사용할 수 있다. (최적화)
const App = () => {
const [color, setColor] = useState("");
const [movie, setMovie] = useState("");
const onChangeHandler = e => {
if (e.target.id === "color") setColor(e.target.value);
else setMovie(e.target.value);
};
return (
<div className="App">
<div>
<label>
What is your favorite color of rainbow ?
<input id="color" value={color} onChange={onChangeHandler} />
</label>
</div>
<div>
What is your favorite movie among these ?
<label>
<input
type="radio"
name="movie"
value="Marriage Story"
onChange={onChangeHandler}
/>
Marriage Story
</label>
<label>
<input
type="radio"
name="movie"
value="The Fast And The Furious"
onChange={onChangeHandler}
/>
The Fast And The Furious
</label>
<label>
<input
type="radio"
name="movie"
value="Avengers"
onChange={onChangeHandler}
/>
Avengers
</label>
</div>
<Info color={color} movie={movie} />
</div>
);
};
const getColorKor = color => {
console.log("getColorKor");
switch (color) {
case "red":
return "빨강";
case "orange":
return "주황";
case "yellow":
return "노랑";
case "green":
return "초록";
case "blue":
return "파랑";
case "navy":
return "남";
case "purple":
return "보라";
default:
return "레인보우";
}
};
const getMovieGenreKor = movie => {
console.log("getMovieGenreKor");
switch (movie) {
case "Marriage Story":
return "드라마";
case "The Fast And The Furious":
return "액션";
case "Avengers":
return "슈퍼히어로";
default:
return "아직 잘 모름";
}
};
const Info = ({ color, movie }) => {
const colorKor = getColorKor(color);
const movieGenreKor = getMovieGenreKor(movie);
return (
<div className="info-wrapper">
제가 가장 좋아하는 색은 {colorKor} 이고, <br />
즐겨보는 영화 장르는 {movieGenreKor} 입니다.
</div>
);
};
- 위 상황의 경우 자식 컴포넌트로
color
와 movie
가 props로 전달되는데, 이 때 color
의 값만 변경되더라도 colorKor
뿐만 아니라 movieGenreKor
함수까지 다시 연산된다.
const colorKor = useMemo(() => getColorKor(color), [color]);
const movieGenreKor = useMemo(() => getMovieGenreKor(movie), [movie]);
- 위와 같이 useMemo를 사용하면 각 함수는 연산한 값을 메모이제이션하게 되고, 의존성 배열에 있는 값이 변경되었을 때만 다시 연산을 실행하게 된다.
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
- 메모이제이션 된 콜백(함수)을 반환하는 훅.
- 리렌더링이 되더라도 특정 함수가 매번 다시 선언될 필요가 없을 경우, 그 함수를 메모이제이션 해뒀다가 사용할 수 있다. (최적화)
const onChangeHandler = useCallback(e => {
if (e.target.id === "color") setColor(e.target.value);
else setMovie(e.target.value);
}, []);
- 위의 예제 상황에서
onChangeHandler
는 한 번 선언되고나면 또 다시 선언될 필요가 없으므로, useCallback
으로 메모이제이션해두고 사용할 수 있다.
- 공식문서에 의하면 콜백을 최적화된 자식컴포넌트에 props로 넘길 때 유용하게 사용할 수 있다.
- 어떤 함수와 완전히 같은 함수는 현재 그 함수 뿐이기 때문에, (memo로 자식컴포넌트가 최적화되었을 때) 전달되는 함수가 다시 선언될 경우 그 함수가 완전히 이전과 같더라도 다른 것으로 취급하고 props가 변경된 것으로 인식해 리렌더링 된다.
- useCallback(fn, deps)은 useMemo(() => fn, deps)와 같다.