컴포넌트 최적화를 위해서는 useMemo와 useCallback을 사용한다.
함수형 컴포넌트도 함수의 일종이기 때문에 렌더링 될 때마다 모든 내부 변수가 초기화 된다.
const value = useMemo(() => {
return calculate();
}, [item]);
※ 무분별하게 사용하면 불필요한 값까지 메모이제이션하면 성능이 저하될 수 있다.
import React, { useMemo, useState } from 'react';
const hardCalculate = (number) => {
console.log('어려운 계산!');
for (let i = 0; i < 999999999; i++) {} // 생각하는 시간
return number + 10000;
};
const easyCalculate = (number) => {
console.log('짱 쉬운 계산!');
return number + 1;
};
function App() {
const [hardNumber, setHardNumber] = useState(1);
const [easyNumber, setEasyNumber] = useState(1);
// const hardSum = hardCalculate(hardNumber);
const hardSum = useMemo(() => {
return hardCalculate(hardNumber);
}, [hardNumber]);
const easySum = easyCalculate(easyNumber);
return (
<div>
<h3>어려운 계산기</h3>
<input
type="number"
value={hardNumber}
onChange={(e) => setHardNumber(parseInt(e.target.value))}
/>
<span> + 10000 = {hardSum}</span>
<h3>쉬운 계산기</h3>
<input
type="number"
value={easyNumber}
onChange={(e) => setEasyNumber(parseInt(e.target.value))}
/>
<span> + 1 = {EasySum}</span>
</div>
);
}
export default App;
어려운 계산기에는 임의로 굉장히 긴 반복문 코드를 넣어놓았기때문에 어려운 계산기의 숫자를 1 증가시키면 1초 정도의 시간이 지난 뒤 값이 바뀐다. 하지만 쉬운 계산기의 숫자를 1 증가시켰을 때는 바로 계산이 완료될 것을 기대하지만 실제로는 똑같이 1초의 딜레이가 생긴다.
그 이유는 easyNumber라는 state의 값이 바뀌면 App 컴포넌트가 렌더링 되고 hardSum 변수가 초기화 되면서 hardCalculate() 함수가 같이 호출되기때문이다.
이를 막기위해 hardNumber가 바뀔때만 hardCalculate()가 실행되도록 useMemo를 사용한다. hardNumber가 바뀌지않으면 App 컴포넌트가 리렌더링 되더라도 변수가 초기화되지않고 메모리에 저장된 값을 계속 사용한다.
import React, { useEffect, useMemo, useState } from 'react';
function App() {
const [number, setNumber] = useState(0);
const [isKorea, setIsKorea] = useState(true);
const location = {
country: isKorea ? '한국' : '외국',
};
useEffect(() => {
console.log('useEffect 호출');
// 오래 걸리는 작업 ...
}, [location]);
return (
<div>
<h2>하루에 몇끼 먹어요?</h2>
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.value)}
/>
<hr />
<h2>어느 나라에 있어요?</h2>
<p>나라: {location.country}</p>
Mbutton onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
</div>
);
}
export default App;
string과 같은 원시(Primitive) 타입 변수인 경우에는 useEffect의 의존성 배열이 잘 동작하지만, 의존성 배열에 객체를 넣으면 location이 바뀌지않고 number만 바뀌었음에도 불구하고 useEffect가 불려진다.
이는 원시타입 변수가 바로 값이 저장되는 반면 객체 타입 변수는 메모리에 객체를 저장하고 그 저장 위치를 참조하기때문이다.
객체 타입은 겉으로는 똑같아 보여도 메모리상의 주소를 저장하고있기때문에 false를 반환한다.
number state가 바뀜 -> App 컴포넌트 리렌더링 -> location 재할당 -> 메모리상 다른 주소에 객체할당 -> useEffect의 의존성 배열 내 location이 변경되었다고 인식
결과적으로 실제로 location을 변경하지는 않았지만 location이 변경되었다고 인식한다.
이를 막기위해서는 App 컴포넌트가 렌더링 될 때 location을 다시 할당하지않도록 하면된다.
import React, { useEffect, useMemo, useState } from 'react';
function App() {
const [number, setNumber] = useState(0);
const [isKorea, setIsKorea] = useState(true);
const location = useMemo(() => {
return {
country: isKorea ? '한국' : '외국',
};
}, [isKorea]);
useEffect(() => {
console.log('useEffect 호출');
// 오래 걸리는 작업 ...
}, [location]);
return (
<div>
<h2>하루에 몇끼 먹어요?</h2>
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.value)}
/>
<hr />
<h2>어느 나라에 있어요?</h2>
<p>나라: {location.country}</p>
Mbutton onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
</div>
);
}
export default App;