이젠 함수형 컴포넌트에서 Hook을 이용해서 memo를 사용할 수 있다. useMemo는 obj(함수든 객체든 뭐든)
와 [...dependencies]
두가지 인자가 들어간다. useMemo는 dependencies중에 바뀐 값이 있으면 obj를 새로 불러와서 리턴값을 넘겨준다. 바뀐 값이 없으면 obj를 새로 불러와서 리턴하지 않고 이전에 메모리제이션된 dependencies를 넘겨준다.
그런데 중요한것은 새로 리턴된 obj를 불러올 경우 referenece까지 다른 obj를 불러오고 기존에 저장되어있는 dependencies를 불러올 경우에는 reference까지 같은 값을 가져다가 준다는 것이다! 요것을 잘기억하고 있자.
그럼 간단한 앱을 만들거다. input에 입력될때마다 2를 곱한값을 넘겨줄거다. 근데 그 사이에 쓸데없는 for loop이 있어서 계산이 조금 지체되는 효과를 주었다. 이 함수를 slowFunction이라고 부르겠다. 그리고 버튼을 클릭할 때 마다 배경화면이 검은색에서 흰색으로 바뀌는 효과를 주었다.
그럼 state가 number, dark 두가지가 필요하다.
코드로 확인해보자
import { useState, useMemo, useEffect } from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.querySelector('#root'));
function App() {
const [number, setNumber] = useState(0);
const [dark, setDark] = useState(false);
const doubleNumber = slowFunction(number);
const themeStyle = {
backgroundColor: dark ? 'black' : 'white',
color: dark ? 'white' : 'black',
};
return (
<>
<input
type="number"
value={number}
onChange={e => setNumber(e.target.value)}
/>
<button onClick={() => setDark(prevDark => !prevDark)}>
Change Theme
</button>
<div style={themeStyle}>{doubleNumber}</div>
</>
);
}
function slowFunction(num) {
console.log('calling slow function');
for (let i = 0; i <= 1000000000; i++) {}
return num * 2;
}
그럼 input에 입력할때 마다 calling slow function이 찍힌다. 근데 문제는 배경화면을 바꿀때에도 slowfunction이 실행된다는 것이다. 이는 이미 잘 아는것 처럼, setDark는 모든것을 다시 랜더링하기 때문이다. 배경화면은 숫자와 아무 상관이 없음에도 숫자가 매번 새롭게 계산되는 상황은 비효율적이다.
이때 useMemo를 쓸수 있다.
const doubleNumber = useMemo(() => {
return slowFunction(number);
}, [number]);
그럼 number가 바뀔 때만 return 값이 호출된다. 바뀌지 않으면 새로 리턴하는 것이 아니라 이전에 메모리에 저장된 값을 준다.
근데 useMemo를 또 다른 상황에서 쓸 수 있다. 바로 reference가 달라서 매번 새롭게 인식되어 랜더링하는 경우다. 이게 무슨말인가 싶어서 바로 코드를 보도록 하자
useEffect(() => {
console.log('theme style');
}, [themeStyle]);
요렇게 될 경우 themeStyle이 변경될때만 theme style
이 로그되어야한다. 근데 숫자만 변경되었을때도 계속 로그가 일어난다.... themeStyle의 실제 내용은 전혀 바뀐게 없음에도 말이다.
도대체 이유가 뭐냐? themeStyle 안에 내용물에 변화가 없다고 해서 같은 themeStyle로 인식하는게 아니다. 숫자가 바뀌면서 새롭게 랜더링 될때마다 themeStyle의 reference가 달라지기 때문에 같은 내용이더라도 다른 obj로 인식하게 된다. obj는 call by refernece이기 때문이다.
따라서 obj 내용이 변함이 없는 경우에는 그 이전의 reference를 가리키는 themeStyle을 불러오게 해야한다.
이때도 useMemo를 사용할 수 있다. useMemo는 앞서 설명했던것 처럼 reference까지 같이 저장하기 때문이다. 그래서 themeStyle도 useMemo에 저장해주자.
const themeStyle = useMemo(() => {
return {
backgroundColor: dark ? 'black' : 'white',
color: dark ? 'white' : 'black',
};
}, [dark]);
이렇게 하면 dark값이 바뀔때만 새로운 obj를 리턴하고 아닌 경우 이전의 reference를 가리키면서 내용도 같은 themeStyle을 우리에게 준다. 그렇게 되면 useEffect는 dark가 안바뀌었을 경우에 themeStyle이 이전과 같은 정체성을 가진 obj라고 인식하면서 arrow안에 있는 함수를 실행하지 않게 된다.
단!, React.memo와 마찬가지로 불필요한 랜더링이 확실하게 보이는 곳에만 딱 국한적으로 사용해야 메모리 손실을 막을 수 있다. 아무생각없이 이곳저곳에서 사용하지는 말자.
출처: https://www.youtube.com/watch?v=THL1OPn72vo
참고: https://reactjs.org/docs/hooks-reference.html#usememo