유튜브 별코딩님의 강의를 보고 작성한 나의 일지
컴포넌트 성능을 최적화하는 hook
useMemo -> Memo = Memoization (메모이제이션)
동일한 값을 리턴하는 함수를 반복적으로 호출되어야 한다면, 맨 처음 값을 계산할 때 해당 값을 메모리에 저장해서 필요할 때마다 꺼내서 재사용하는 기능을 가지고 있다.
간단히 말하자면, 자주 필요한 값을 맨 처음 계산할 때 캐싱을 해둬서 그 값을 필요할 때마다 캐시에서 꺼내서 사용한다.
기본 형식
첫 번째 인자로는, 콜백함수
두 번째로는 배열을 받는다.
첫 번째 인자인 콜백함수는 우리가 Memoization 할 값을 계산해서 리턴해주는 함수
두 번째 배열은 의존성 배열이라고 불린다.
useMemo 는 안의 요소의 값이 업데이트 될 때마다 콜백함수를 호출해서 Memoization 된 값을 업데이트해서 다시 Memoization 해준다.
빈 배열일 경우, 맨 처음 컴퓨터가 마운트 되었을 때만 계산하고 이후에는 Memoization 된 값만 꺼내서 쓴다.
예 ) 계산기
App.js
import { 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;
};
export default 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, 10))}
/>
<span> + 10000 = {hardSum}</span>
<h3>쉬운 계산기</h3>
<input
type="number"
value={easyNumber}
onChange={(e) => setEasyNumber(parseInt(e.target.value, 10))}
/>
<span> + 1 = {easySum}</span>
</div>
);
}
위의 코드를 보면, 저장할 함수를 useMemo 콜백함수에 저장해서 계산할 때마다 업데이트가 되도록 설계되어있다.
useState 를 사용해서 input 안에 값이 업데이트 될때마다 state 가 바뀌도록 설계했다.
숫자를 올리면...
이렇게 값이 잘 바뀐것을 확인할 수 있다.
다른 예제 )
App.js
import { useState, useEffect, useMemo } from "react";
export default function App() {
const [number, setNumber] = useState(0);
const [isKorea, setIsKorea] = useState(true);
// const location = isKorea ? "한국" : "외국";
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>
<button onClick={() => setIsKorea(!isKorea)}>비행기 타자</button>
</div>
);
}
이번에는 useMemo 에 객체 형태에 삼항 연산자를 사용해서 state 가 true 이면, '한국'이 뜨고 false 이면 '외국'이 뜨게 설계했다.
비행기 타자 버튼을 누를때마다, 나라가 바뀐다.
자바스크립트에서는 기본형과 참조형이 존재한다.
변수에 기본형인 숫자 1을 담고, 다른 변수에 똑같이 숫자 1을 담아 서로 비교 연산자를 진행하면...
const num = 1
const num1 = 1
num === num1
결과는...
하지만 참조형중 하나인 객체를 변수에 저장하고 똑같은 객체를 하나 더 만들고 비교 연산자를 하면...
const obj = {apple : "아이폰"}
const obj1 = {apple : "아이폰"}
obj === obj1
결과는...
그 이유는 처음에 저장되는 객체는 다른 번호로 저장되어있어, 아무리 똑같은 객체가 담긴 변수여도 비교 연산자를 하면 false 가 된다.
출처 : https://velog.io/@altjsvnf/React-useMemo
useMemo 와 동일하게 컴포넌트 성능을 최적화시킨다.
이 때 Memoization 이란???
useCallback( ( ) => {
return value;
}, [item])
useMemo 랑 다르게, useCallback 은 이름에서 알 수 있듯이 인자로 전달한 콜백함수 그 자체를 Memoization 해준다. 렌더링 될 때마다, 이전에 저장해둔 함수 객체를 재사용한다.
두 번째 인자인 의존성 배열 내부에 있는 값이 변경되니 않는 이상, 다시 초기화되지 않는다.
예 )
App.js
import { useEffect, useState, useCallback } from "react";
export default function App() {
const [number, setNumber] = useState(0);
const [toggle, setToggle] = useState(true);
const someFunction = useCallback(() => {
console.log(`someFunc: number ${number}`);
return;
}, [number]);
useEffect(() => {
console.log("sumFunction이 변경되었습니다.");
}, [someFunction]);
return (
<div className="App">
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.value)}
/>
<button onClick={() => setToggle(!toggle)}>{toggle.toString()}</button>
<br />
<button onClick={someFunction}>Call someFunc</button>
</div>
);
}
다섯번 클릭했을 경우...
콘솔 로그...
숫자를 바꿀 때마다 "sumFunction이 변경되었습니다." 이 호출되면서
Call someFunc 버튼을 누르면...
"someFunc: number 5" 이 콘솔에 찍힌다.
useCallback 을 사용하지 않으면, 숫자를 바꿀때마다 렌더링되면서
"someFunc: number 숫자" 가 호출되었다.
하지만, 이 훅을 통해 오직 number 라는 state 가 바뀌고 버튼을 클릭했을 때마다, 콘솔을 호출할 수 있도록 설계되어있다.
다른 예 )
App.js
import { useCallback, useState } from "react";
import Box from "./Box";
import "./styles.css";
export default function App() {
const [size, setSize] = useState(100);
const [isDark, setIsDark] = useState(false);
const createBoxStyle = useCallback(() => {
return {
backgroundColor: "pink",
width: `${size}px`,
height: `${size}px`
};
}, [size]);
return (
<div
style={{
background: isDark ? "black" : "white"
}}
>
<input
type="number"
value={size}
onChange={(e) => setSize(e.target.value)}
/>
<button onClick={() => setIsDark(!isDark)}>Change Theme</button>
<Box createBoxStyle={createBoxStyle} />
</div>
);
}
Box.js
import { useEffect, useState } from "react";
function Box({ createBoxStyle }) {
const [style, setStyle] = useState({});
useEffect(() => {
console.log(`박스 키우기 🎁`);
setStyle(createBoxStyle());
}, [createBoxStyle]);
return <div style={style}></div>;
}
export default Box;
App 컴포넌트 안에 Box 라는 컴포넌트를 만들어서 input 의 숫자를 증가시킬 때마다, 핑크색 박스가 점점 커지고 숫자를 줄일때마다 박스는 작아진다.
숫자 300일 때...
숫자 50일 때...
useCallback 을 사용해서 함수 객체를 저장시키고, 렌더링 될 때마다 저장된 함수를 꺼내서 재사용해서 사용된다.
그리고 Change Theme 을 클릭하면...
검정색 박스가 생기게 된다.
이렇게 해서 나머지 hooks 를 공부하고 있어서, 다음에는 useReduer 에 대해 알아보겠다.