앞에서 다뤘듯이 React 16.8에 새롭게 추가된 개념인 Hook API에는 가장 기본적인 useState
, useEffect
, useContext
이 있었다. 그 외에도 추가적인 Hooks들이 있는데 그 중에서 함께 자주 언급되고 많이 사용되고 있는 useMemo, useCallback을 자세히 다뤄볼 것이다 🧐
React 공식문서에 적혀있는 useMemo의 기본 구조는 다음과 같다.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
성능 최적화를 위해 도입된 useMemo
는 메모이제이션된 값을 반환하며, 특정 값이 변경되었을 때만 입력한 연산을 실행하고, 값이 그대로라면 직전에 연산했던 결과를 그대로 다시 사용하는 방법이다.
또한 useMemo
를 통해 전달된 연산(함수)는 렌더링을 하는 중에 실행되기 때문에 렌더링 중에는 하지않는 Side Effect는 useMemo
가 아닌 useEffect
에서 실행되어야 한다!
⚡Side Effect 란?
컴포넌트가 화면에 렌더링된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 의미한다.
import React, { useState, useMemo} from "react";
const App = () => {
const [name, setName] = useState("");
const nameLength = useMemo(()=> name.length, [name]);
const updateName = (e) => {
setName(e.target.value);
};
return (
<div>
<input onChange={updateName}></input>
<h3>이름의 길이는 {nameLength} 입니다.</h3>
</div>
);
};
export default App;
input을 통해 이름을 입력받고 useState를 이용해 이름을 변경해주고, 이름이 변경될 때 마다 useMemo를 이용하여 이름의 길이를 측정하는 간단한 예제이다.
const nameLength = useMemo(()=> name.length, [name]);
핵심 코드를 보면 nameLength라는 메모리제이션 될 값을 선언하고 useMemo를 이용하여 name의 변경 여부를 확인하며 변경시에는 name.length를 이용하여 길이 계산하고 nameLength에 대입하게 된다.
React 공식문서에 적혀있는 useCallback의 기본 구조는 다음과 같다.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback
역시 마찬가지로 성능 최적화를 위해 도입되었으며 메모이제이션된 콜백을 반환한다. 즉 원하는 때에만 콜백 함수를 생성할 수 있다. useCallback
의 첫번째 파라미터에는 생성하고 싶은 함수를 적고, 두번째 파라미터에는 어떠한 값을 넣게 되는데, 이 값이 변할때 첫번째 파라미터의 함수가 생성되는 구조이다.
import React, { useState, useCallback} from "react";
const App = () => {
const [name, setName] = useState("");
const [nameLength, setNameLength] = useState(0);
// 첫 렌더링시에만 함수가 생성된다.
const updateName = useCallback((e)=> {
setName(e.target.value);
},[]);
// name값이 변경될 때에만 함수가 생성된다.
const updateNameLength = useCallback(()=>{
setNameLength(name.length);
},[name]);
return (
<div>
<input onChange={updateName}></input>
<button onClick={updateNameLength} >입력</button>
<h3>이름의 길이는 {nameLength} 입니다.</h3>
</div>
);
};
export default App;
input을 통해 이름을 입력받고 button을 누르면 이름의 길이를 useState
를 이용하여 업데이트 해주는 예제이다.
// 리렌더링 될 때 마다 생성된다.
const updateName = (e) => {
setName(e.target.value);
};
// 첫 렌더링시에만 함수가 생성된다.
const updateName = useCallback((e)=> {
setName(e.target.value);
},[]);
핵심 코드를 보면 useMemo
의 예제에서의 updateName 함수와는 다르게 이번 예제에서는 useCallback을 사용하여 빈 배열 값을 넣어줬기 때문에 첫 렌더링시에만 해당 함수가 생성된다.
// name값이 변경될 때에만 함수가 생성된다.
const updateNameLength = useCallback(()=>{
setNameLength(name.length);
},[name]);
button을 클릭 할 시 수행되는 함수인 updateNameLength의 경우에는 배열에 name
값을 넣어주었기 때문에 name
값이 변경될 때에만 함수가 생성되게 된다.
useMemo : 렌더링하는 중에 특정 값이 변경되었을 때만 전달된 연산을 실행하고, 값이 그대로라면 그 전에 했던 연산 결과를 그대로 다시 사용한다. 즉 전달된 함수가 실행되고 반환된 결과를 저장한다.
useCallback : 사용자가 원하는 때에만 전달되는 함수를 생성할 수 있으며, 전달된 함수 그 자체를 저장한다.