React

[React] Hooks: useMemo, useCallback, useRef

래모 2022. 6. 5. 14:27

// 리액트를 다루는 기술 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;

 

✳️ 사용한 메서드

  1. arr.reduce( (누적값, 현재값, 인덱스, 요소 ) => { return 결과 }, 초깃값 )
    • 배열의 각 요소를 순회하며 반복전인 연산을 하는 메서드
    • 인덱스, 요소, 초깃값은 생략 가능
  2. 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;