[React] Hooks: useMemo, useCallback, useRef
// 리액트를 다루는 기술 8장
지난 포스팅에 이어 작성해유
4️⃣ useMemo
: 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있는 Hook
useMemo는 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용합니다.
기본 형태
const a = useMemo(function, deps)
- function : 어떻게 연산할 지 정의하는 함수
- deps : 검사할 특정 값을 담은 배열
예시로 숫자들의 평균을 보여주는 함수형 컴포넌트를 일단 useState를 사용하여 작성!
import React, {useState} from 'react';
const getAverage = numbers => {
console.log('평균값 계산 중...');
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a,b) => a+b);
// a는 누적값 b는 numbers라는 배열에 요소
// 즉, numbers에 있는 요소들을 다 더한 값을 a에 계속 더하고 sum이라는 변수에 넣음
return sum/ numbers.length;
}
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const onChange = e => {
setNumber(e.target.value);
};
const onInsert = e => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
};
return (
<div>
<input value = {number} onChange = {onChange} />
<button onClick = {onInsert}> 등록 </button>
<ul>
{list.map((value, index) => (
<li key = {index}> {value} </li>
))}
</ul>
<div>
<b>평균값: </b> {getAverage(list)}
</div>
</div>
)
}
export default Average;
✳️ 사용한 메서드
- arr.reduce( (누적값, 현재값, 인덱스, 요소 ) => { return 결과 }, 초깃값 )
- 배열의 각 요소를 순회하며 반복전인 연산을 하는 메서드
- 인덱스, 요소, 초깃값은 생략 가능
- arr1.concat( arr2 )
- 문자열을 합치는 메서드
- 즉, arr1 + arr2 를 하여 새로운 리스트를 만듬
위의 코드를 useMemoe를 사용하면 인풋 내용이 바뀔 때마다 평균값을 다시 계산하지 않도록 즉, 렌더링할 때마다 계산하지 않도록 할 수 있음!
( 즉, useMemo를 사용하기 전에는 버튼을 누르기 전 숫자를 입력하기만 해도 함수를 불러오지만 useMemo를 사용한 후에는 버튼을 눌러야 함수를 불러옴 ) 근데 이건 onChange를 안 쓰면 되지 않나?
...
const Average = () => {
...
const avg = useMemo(() => getAverage(list), [list]);
return (
...
<b>평균값: </b> {avg}
...
)
}
export default Average;
5️⃣ useCallback
useMemo와 상당히 비슷한 함수!!
const a = useCallback(function, [deps]);
- function : 생성하고 싶은 함수
- deps : 검사할 특정 값을 담은 배열
...
const Average = () => {
...
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}, [number, list]); // number 혹은 list가 바뀌었을 때만 함수 생성
...
}
export default Average;
✳️ useMemo와 useCallback의 차이점?
: 숫자, 문자열, 객체처럼 일반 값을 재사용하려면 useMemo
: 함수를 재사용하려면 useCallback
이 차이점을 제외하곤 차이점이 거의 존재하지 않음
참고 블로그
https://www.zigae.com/react-memo/
리액트 useCallback, useMemo 언제 사용 할까?
본글은 useCallback, useMemo에 대해 설명하는 글이 아님을 알린다.
www.zigae.com
6️⃣ useRef
: 함수형 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해줌
✳️ ref란?
: html에서 DOM요소에 이름을 달 때 id라는 고유값을 사용하듯이 리액트에서도 DOM요소에 이름을 붙이는 방법을 ref라고 함
➡️ DOM을 직접 건드려야 할 때 사용( ex. input에 포커스 주기, 스크롤 박스 조작하기 Canvas요소에 그림 그리기 등 )
아까 사용했던 코드를 useRef를 사용하여 등록 버튼을 눌렀을 때 포커스가 인풋 쪽으로 넘어가도록 코드를 작성해보면 다음과 같다.
import React, {useState , useMemo, useCallback} from 'react';
const getAverage = numbers => {
console.log('평균값 계산 중...')
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a,b) => a+b);
return sum/ numbers.length;
}
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null); // ref 객체를 만들어줌
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus();
// ref 객체의 current 값은 우리가 선택하고자 하는 DOM을 가리킴
// 그리고 포커싱을 해주는 DOM API focus()를 호출함
}, [number, list]);
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value = {number} onChange = {onChange} ref = {inputEl}/>
// 선택하고 싶은 DOM에 속성으로 ref 값을 설정
<button onClick = {onInsert}> 등록 </button>
<ul>
{list.map((value, index) => (
<li key = {index}> {value} </li>
))}
</ul>
<div>
<b>평균값: </b> {avg}
</div>
</div>
)
}
export default Average;