useMemo는 이전 값을 기억해서 성능을 최적화하는 용도로 사용된다. useMemo에서
memo
는memoization
을 의미한다. 프로그램이 동일한 연산을 반복해야 할 때, useMemo hook에 의존하고 있는 배열인의존성배열
에 입력한 값이 변하지 않는다면 기존에 연산해두었던 값을 재사용하는 방식으로 성능을 최적화 한다.
const value = useMemo(() => {
return myFunction()
},[values])
useMemo는 두가지의 파라미터를 받는다. 첫번째는 콜백함수
이고, 두번째는 의존성배열
이다.
첫번째 인자인 콜백함수의 반환값이 useMemo의 반환값이 되고, 두번째 인자인 의존성배열 내부의 값이 업데이트 될 때만 콜백함수를 재실행하여 메모이제이션해두었던 값을 업데이트한다.
useEffect의 의존성배열과 마찬가지로, useMemo 역시 의존성배열이 빈배열일때는 최초 렌더링될 때 한번만, 생략할 경우 모든 렌더링마다 실행, 배열에 값이 있을때에는 값이 업데이트 될 때만 실행된다.
Q :
왜 easy calculate의 input에만 1씩 증가시켰는데 결과값이 리턴되는데 1초 이상이 걸릴까...?
import './App.css'
import {useState, useRef, useEffect, useMemo} from 'react'
export default function App() {
const [hardNumber, setHardNumber] = useState(1);
const [easyNumber, setEasyNumber] = useState(1);
const hardCalculate = number => {
console.log('어려운계산');
for (let i =0; i<1000000; i++) {} // 생각하는 시간
return number + 1000;
}
const easyCalculate = number => {
console.log('쉬운계산');
return number + 1;
}
const hardSum = hardCalculate(hardNumber);
const easySum = easyCalculate(easyNumber);
return (
<div>
<h1>hard calculate</h1>
<input type="number" onChange={e => setHardNumber(parseInt(e.target.value))}/>
<span> + 1000 = {hardSum}</span>
<br/>
<br/>
<h1>easy calculate</h1>
<input
type="number"
value={easyNumber}
onChange={e => setEasyNumber(parseInt(e.target.value))}
/>
<span> + 1 = {easySum} </span>
</div>
)
}
A :
App 컴포넌트는 함수형 컴포넌트이다. 즉, input에 값을 입력하면 easyNubmer와 관련된 useState의 세터가 변경되고 -> App 컴포넌트가 리렌더링되고 -> easySum 변수와 hardSum 변수가 모두 초기화 되는것!!
hardCalculate
함수는 for 문을 1000000번 실행하고 난 뒤, return 하는 함수이다. 또한, easy calculate의 input 값을 입력하기만 해도 useState의 세터 함수가 변경
되어, 컴포넌트가 리렌더링
되어 hardNumber 또한 리렌더링 되고, hardCalculate 함수의 무거운 연산
을 다시 하게 된다.
콘솔로 확인해보면, easy calculate 의 input에만 값을 입력했는데도 hard calculate
함수가 계속해서 재 실행되는 모습이다.
easyNumber state를 변경했을때 hardSum 함수가 호출되지 않게 할 수 없을까?
import './App.css'
import {useState, useRef, useEffect, useMemo} from 'react'
export default function App() {
const [hardNumber, setHardNumber] = useState(1);
const [easyNumber, setEasyNumber] = useState(1);
const hardCalculate = number => {
console.log('어려운계산');
for (let i =0; i<1000000; i++) {} // 생각하는 시간
return number + 1000;
}
const easyCalculate = number => {
console.log('쉬운계산');
return number + 1;
}
// 1️⃣ hardNumber의 값이 변경될 경우에만 hardSum 변수를 초기화한다
// easyNumber의 값을 변경하더라도, hardSum은 useMemo를 통해 메모이제이션한 값을
// 사용할 것이며, hardNumber가 변경될 경우에만 기존의 메모이제이션을
// 새로 계산한 값으로 대체한다.
const hardSum = useMemo(() => {
return hardCalculate(hardNumber)
},[hardNumber]); // 의존성 배열 값이 변경되어야만 return문의 내용이 초기화된다.
// 2️⃣ 마찬가지로 easySum 역시 콜백함수의 결과값을 메모이제이션 해 놓는다.
const easySum = useMemo(() => {
return easyCalculate(easyNumber)
},[easyNumber])
return (
<div>
<h1>hard calculate</h1>
<input type="number" onChange={e => setHardNumber(parseInt(e.target.value))}/>
<span> + 1000 = {hardSum}</span>
<br/>
<br/>
<h1>easy calculate</h1>
<input
type="number"
value={easyNumber}
onChange={e => setEasyNumber(parseInt(e.target.value))}
/>
<span> + 1 = {easySum} </span>
</div>
)
}
useMemo에서 주어진 조건이 만족되지 않았다면, 컴포넌트가 리렌더되더라도 메모이제이션한 값이 초기화되지 않고 유지된다. 주어진 조건의 만족이란 의존성 배열의 값이 변경되었을 경우를 말한다.