프론트엔드/React

useMemo, useCallback에 대해

journey-dev 2023. 12. 10. 18:42

useMemo

  • 계산 비용이 높은 연산 결과 "값"을 메모이제이션 함.
  • 해당 값이 변경되지 않는 한 다시 계산하지 않아, 렌더링 중에 불필요한 계산을 방지함
  • 탑레벨에서 한번 호출되야 함. (hook은 무조건 한번은 호출되야 해서)
  • 형태 
const memoizedValue = useMemo(() => {
  return computeExpensiveValue(a, b);  // 계산 비용이 높은 연산 또는 값
}, [a, b]);

 

  • 사용 : 복잡한 계산 처럼 비용이 높은 연산을 피하고 싶을때 사용함.

 

 calculateValue

- 초기 렌더시 한번 호출됨

- 순수 함수여야 함

- 인자를 받지 않음 ( useMemo( (   )  =>  ), []);

- 캐시하길 원하는 값을 계산하는 함수

- 캐시하길 원하는 값을 반환해야 함

- dependencies값이 바뀌지 않으면 같은 값을 다시 리턴한다.

 

 dependencies

- [dep1, dep2, dep3]

- Object.js()로 이전 이후 값이 같은지 여부를 판단함. (referrence type의 경우는 참조값이 같은지를 판단함)

 

 return

- 콜백함수를 호출한 결과를 반환


useCallback

  • "함수"를 메모이제이션 함
  • 형태
const memoizedCallback = useCallback(
  () => {doSomething(a, b)}, // 콜백 함수 
  [a, b], // dependency
);
  • 사용 : 주로 자식 컴포넌트에 props 전달로 인해, 함수가 불필요하게 다시 생성되는 것을 방지할 때 쓰임 

 

✅ fn

- 인자를 받을 수 있음  ( useCallback( (arguments)  =>  ), []);

- 초기 렌더시 함수가 반환된다. 함수가 호출되는 것은 아님!

- 함수가 호출되진 않으므로, 함수 호출 시기와 호출 여부를 직접 결정할 수 있음

 

 dependencies

 - Object.js()로 이전 값과 비교함

- 콜백함수 안에서 참조하고 있는 state나 props가 될 확률이 높으나, 절대적인건 아님

 

 return

- 이전 함수와 다른 메모리 주소를 갖는 새로운 함수를 반환함.

 


💡 useEffect와의 차이

useMemo나 useEffect나 둘다 처음 렌더시 한번 실행되고, dependency 변경이 있을때 동작하게 되는데

이 둘의 차이가 뭔지 헷갈림

 

간단히 정리하자면

- useMemo : 값이 반환됨

- useCallback: 함수가 반환됨

- useEffect : 함수가 실행될 뿐임.

 

 ※ useEffect 

  • 사용
  • 비동기 작엄, 부수 효과를 처리하기 위한 훅, 컴포넌트 렌더링 외부에서 발생하는 작업들을 처리함
  • 데이터 패칭, 네트워크 요청 등과 같은 비동기 작업, dom 조작을 다룰 떄
  • 실행 시점
  • 컴포넌트가 렌더링 완료된 후에 DOM이 업데이트 된 직후에 실행됨.
  • 렌더링 완료된 후에 비동기 작업이나 특정 효과를 수행할 때 사용됨

 

💡 useMemo, useCallback의 dependency가 빈배열 이라면?

: 처음 렌더시 한번만 실행된 뒤, 다시 실행되지 않고 이전에 생성된 값을 계속 재사용함

 


💡 예제 코드

import { useCallback, useMemo, useState } from "react";

const getAverage = (list) => {
  console.log("평균 계산");
  if (list.length === 0) return 0;
  return list.reduce((acc, cur) => +acc + +cur, 0) / list.length;
};

function Average() {
  const [inputValue, setInputValue] = useState("");
  const [valueList, setValueList] = useState([]);

  const onChange = useCallback((e) => setInputValue(e.target.value), []); // useCallback는 인자 받을 수 있음

  const onClick = useCallback(() => {
    const updateList = valueList.concat(inputValue);
    setValueList(updateList);
    setInputValue("");
  }, [inputValue, valueList]);
  // onClick함수는 inputValue와 valueList를 조회해서 새 list를 생성하기 때문에, dependency에 두 state값이 포함되야함.

  return (
    <>
      <input type="number" value={inputValue} onChange={onChange} />
      <button type="button" onClick={onClick}>
        등록
      </button>
      <ul>
        {valueList?.map((v, index) => {
          return <li key={index}>{v}</li>;
        })}
      </ul>
      <b>평균값 : {useMemo(() => getAverage(valueList), [valueList])}</b> {/* useMemo는 인자 받을 수 없음 */}
    </>
  );
}

export default Average;

 

 

 

 


https://react.dev/reference/react/useMemo