본문 바로가기

React/공부공부

[React Hook] UseMemo, UseCallback

 

 

[ UseMemo ]

함수의 결과를 메모이제이션.

=> 의존값이 변경되지 않으면 함수의 재연산 없이 메모제이션된 값을 반환.

 

아래처럼 배열 arr에 대해, 배열의 합을 구하는 함수 sum 이 있을때,

  const [arr, setArr] = useState([1, 2, 3, 4, 5]);
  const [count, setCount] = useState(0);

  // 배열합
  const sum = arr.reduce((a, b) => a + b);
  console.log("배열합계산");

  // 카운트클릭
  const handleClick = () => {
    setCount((prev) => prev + 1);
  };
  
  // 배열값추가
  const addArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      let max = Math.max(...copy);
      return [...prev, max + 1];
    });
  };
  
  // 배열값변경
  const changeArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      copy[copy.length - 1] += 1;
      return [...copy];
    });
  };

  return (
    <>
      <div>
        <div>배열 : {arr}</div>
        <div>배열합 : {sum}</div>
        <div>
          <button onClick={handleClick}>카운트 : {count}</button>
        </div>
        <div>
          <button onClick={addArr}>배열값추가</button>
          <button onClick={changeArr}>배열값변경</button>
        </div>
      </div>
    </>
  );

 

'카운트' 버튼 클릭 =>  count증가, sum 함수 실행.

'배열값추가' 버튼 클릭 => sum 함수 실행.

'배열값변경' 버튼 클릭 => sum 함수 실행.

count가 state값으로 지정되어 있어 버튼을 누를때마다 sum함수도 함께 실행되고 불필요한 같은 연산을 반복하게 된다.

sum함수가 배열값이 추가/변경될때만 실행되도록 useMemo 적용.


* UseMemo *

첫번째인자 => 연산결과를 반환하는 함수 (sum)

두번째인자 => 의존성배열 (arr, arr.length)   _ 이 값들이 변경될때만 함수 실행(재연산)

* 배열은 참조객체이기 때문에 배열 요소값을 직접 변경하는 경우에는(ex) arr[0] = 0) 배열의 변경사항을 감지하지 못할 수 있다.  따라서 arr.length도 의존성으로 추가. 아래 예시에서는 arr참조값을 바꿔줬기 때문에 없어도 잘 됨. 그냥 참고

  const [arr, setArr] = useState([1, 2, 3, 4, 5]);
  const [count, setCount] = useState(0);

  // ********************* UseMemo *********************
  const sum = useMemo(() => {
    console.log("배열합계산");
    return arr.reduce((a, b) => a + b);
  }, [arr, arr.length]);

  // 카운트클릭
  const handleClick = () => {
    setCount((prev) => prev + 1);
  };
  
  // 배열값추가
  const addArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      let max = Math.max(...copy);
      return [...prev, max + 1];
    });
  };
  
  // 배열값변경
  const changeArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      copy[copy.length - 1] += 1;
      return [...copy];
    });
  };

  return (
    <>
      <div>
        <div>배열 : {arr}</div>
        <div>배열합 : {sum}</div>
        <div>
          <button onClick={handleClick}>카운트 : {count}</button>
        </div>
        <div>
          <button onClick={addArr}>배열값추가</button>
          <button onClick={changeArr}>배열값변경</button>
        </div>
      </div>
    </>
  );

 

'카운트' 버튼 클릭 =>  count증가, sum 함수 실행.

'배열값추가' 버튼 클릭 => sum 함수 실행.

'배열값변경' 버튼 클릭 => sum 함수 실행.

 

 


 

[ UseCallback ]

콜백함수를 메모제이션

=> 의존값이 변경되지 않으면 함수의 재생성 없이 메모제이션된 함수를 반환.

 

'카운트' 버튼을 클릭하면 count값이 증가되는 handleClick() 함수에 대해서,

  const [arr, setArr] = useState([1, 2, 3, 4, 5]);
  const [count, setCount] = useState(0);

  // 배열합
  const sum = useMemo(() => {
    return arr.reduce((a, b) => a + b);
  }, [arr, arr.length]);

  // 카운트클릭
  const handleClick = () => {
    setCount((prev) => prev + 1);
  };
  
  // ************* 카운트클릭 함수 생성확인 *************
  
  const copyFunc = useRef(null);
 
 if (copyFunc.current != handleClick) {
    console.log("새로 생성된 함수");
    copyFunc.current = handleClick;
  } else {
    console.log("기존 함수");
  }
  
  
  // 배열값추가
  const addArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      let max = Math.max(...copy);
      return [...prev, max + 1];
    });
  };
  
  // 배열값변경
  const changeArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      copy[copy.length - 1] += 1;
      return [...copy];
    });
  };

  return (
    <>
      <div>
        <div>배열 : {arr}</div>
        <div>배열합 : {sum}</div>
        <div>
          <button onClick={handleClick}>카운트 : {count}</button>
        </div>
        <div>
          <button onClick={addArr}>배열값추가</button>
          <button onClick={changeArr}>배열값변경</button>
        </div>
      </div>
    </>
  );

 

어떤 버튼을 클릭해도 handleClick함수가 재생성된다. ( 콘솔창에 "새로 생성된 함수" 출력 )

불필요한 함수 생성을 막기 위해 UseCallback 사용. 

 


 

* UseCallback *

첫번째인자 => 콜백 함수 (handleClick)

두번째인자 => 의존성배열 ( [ ] )   _ 이 값들이 변경될때만 함수 생성, 빈 값인 경우 초기 렌더링 시 한번만 생성

  const [arr, setArr] = useState([1, 2, 3, 4, 5]);
  const [count, setCount] = useState(0);

  // 배열합
  const sum = useMemo(() => {
    return arr.reduce((a, b) => a + b);
  }, [arr, arr.length]);

  // ********************* UseCallback *********************
  const handleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);
  
  // ************* 카운트클릭 함수 생성확인 *************
  
  const copyFunc = useRef(null);
 
 if (copyFunc.current != handleClick) {
    console.log("새로 생성된 함수");
    copyFunc.current = handleClick;
  } else {
    console.log("기존 함수");
  }
  
  
  // 배열값추가
  const addArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      let max = Math.max(...copy);
      return [...prev, max + 1];
    });
  };
  
  // 배열값변경
  const changeArr = () => {
    setArr((prev) => {
      let copy = [...prev];
      copy[copy.length - 1] += 1;
      return [...copy];
    });
  };

  return (
    <>
      <div>
        <div>배열 : {arr}</div>
        <div>배열합 : {sum}</div>
        <div>
          <button onClick={handleClick}>카운트 : {count}</button>
        </div>
        <div>
          <button onClick={addArr}>배열값추가</button>
          <button onClick={changeArr}>배열값변경</button>
        </div>
      </div>
    </>
  );

 

초기 렌더링 시 => handleClick 함수 생성.  ( 콘솔창에 "새로 생성된 함수" 출력 )

버튼 클릭 =>  기존 handleClick 함수 사용.  ( 콘솔창에 "기존 함수" 출력 )

 

 

 


 

* UseCallback 의존성 배열

=> 함수의 실행 결과에 직접적으로 영향을 끼치는 값으로 지정. 

 

* UseCallback 이 필요한 경우

 - 부모 컴포넌트의 렌더링에 의한 자식 컴포넌트의 렌더링을 방지하고자 할 때.

 - 계산 비용 / 호출 비용이 큰 함수의 최적화.