본문 바로가기

React Native

React #7 - 리엑트훅(useMemo, useCallback)

성능 최적화를 할 수 있는!

고도화 작업을 할 수있는 리액트 훅 

 

 

useMemo
객체 or 함수에 씌워줄 수 있다 : 어떠한 '값'을 memoize
1. 함수의 실.행.결.과 를 memoization 한다! 함수를 수행할 매개변수 값이 변화하지 않는 경우 == 결과값이 같은 경우에는 렌더링X
2. 객체(참조) 때문에계속해서 새로운 객체가 생성되는 것을 방지하면서, 객체 내의 value 값이 변화하는 경우에만 렌더링

 

 

import { useMemo, useState } from "react";

// 지정한 수가 소수인지 여부를 반환
var isPrime = function (num) {
  console.time('소요시간');
  // 소수 판별 코드
  let prime = true;

  for (let i = 2; i <= num / 2; i++) {
    if (num % i === 0) {
      prime = false;
      break;
    }
  }

  if(num === 1){
    prime = false;
  }else if(num === 2){
    prime = true;
  }
  console.timeEnd('소요시간');
  return prime;
};


function App() {
  const [name, setName] = useState('GD');
  const [num , setNum] = useState(1);

  const result = useMemo(()=> isPrime(num), [num]);
  //[num] => num이 바뀌는지 확인함
  //num이 안바뀌고 name만 바뀌어도 isPrime 계산 다시 안함!
  //반환값을 기억하고 있는 것이다!


  return (

    <>
      <h1>useMemo Hook - 상태가 변경되지 않으면 함수를 호출하지 않고 메모이징된 결과값 사용 </h1>
      <div>
        <input type="text" value = {name} onChange={e => setName(e.target.value) }/>
        가 좋아하는 숫자:
        <input type="number" min="1" max="1000000007" value = {num} onChange={e => setNum(e.target.value) }/>
        <div>결과 : {name}{num}은 소수가 { result ? <span style={{color:'blue'}}>맞습니다.</span> : <span style={{color:'red'}}>아닙니다.</span>}</div>
      </div>
    </>
  );
}

export default App

 

 - const result =  useMemo(() => {} , [num)

 - useMemo의 첫 번째 매개변수는 계산을 수행할 함수

 - 두 번째 매개변수는 dependencies, 값이 변화하면 계산함수를 수행할 ...

* 계산된 값이 변경하지 않으면 계산을 다시 하지 않고 기억해둔 cache 값을 가져와서 hit

 

 

 

 

예제1. app 내에 두개의 계산기

 

App 내에 두가지 상태가 있고, 이 두가지 상태는 각각의 계산기별 연산을 수행한다.

1번 hardCaclculator의 함수가 굉장히 복잡한 구조를 가지고있어 심한 delay를 가져오는 경우,

2번 easyCaclculator만 변경하였음에도 App 전체가 리렌더링이된다. 

ㄴ> 이를 막기 위해 hardCaclculator에 useMemo를 씌워준다. 

 

 

 

 


 

 

예제 2. 바꾸고자 하는 값이 '참조' 하는 값인 경우

useEffect는 본래, 의존 배열로 location을 받으면 location이 변화하지 않는 이상 렌더링하지 않아야 맞지만,

'객체' 는 값이 들어있는 것이 아니라, '주소를 참조' 하는 것이기 때문에, 매 렌더링마다 생성되는 location의 값(주소)이 달라진다고 볼 수 있다. 

 

이와 같은 경우 useEffect가 소용이 없으므로, useMemo를 사용해주어야 한다. 

일반적으로 객체를 저장하던 location에 

 

const location = useMemo(()=>{

   return {

      country : isKorea ? '한국' : '외국';

   }

}, [isKorea])

--> 객체의 isKorea값이 변환 되었을 때만 location에 저장하게 되므로,

값이 변화하지 않았을 때에는 렌더링이 일어나지 않는다! 

 

 

 

 

 

 

 

useCallback : '함수' 자체를 memoization 해주는 것이다. 

 


function App() {
  const price = 12000;
  const defaultShippingFees = 3000;
  const [quantity, setQuantity] = useState(1);
  const [shippingFees, setShippingFees] = useState(3000);

  //수량이 변경될 때 배송비를 재계산한다.
  const handleChange = e =>{
    const newQuantity = Number(e.target.value);
    setShippingFees(defaultShippingFees * Math.ceil(newQuantity / 5));
    setQuantity(newQuantity);
  }

  const handleClick = () =>{
    alert(`${price * quantity +shippingFees}원 결제하시겠습니까? `);
    //결제 로직...
  };

>> APP 안에 있는 handleClick, handleChange함수는 리렌더링 될 때마다 새로운 객체로 생성이되는 함수인 것이다. 

 

 

 

 const handleClick = useCallback(() =>{
    alert(`${price * quantity +shippingFees}원 결제하시겠습니까? `);
    //결제 로직...
  }, []);

함수를 다시 생성해야하는 경우 dependencies에 []를 넘겨주면 된다. 

 

 

 

 

 

예제1. 상자의 크기를 변경해주는 프로그램

 

App 컴포넌트에 style 속성을 객체로 리턴해주는 createBoxStyle이 있다. 

해당 함수는 Box 컴포넌트에 이 함수를 넘긴다. 

 

 

 

Box 컴포넌트에서는, 

 

style을 적용해주는 함수에 useEffect를 통해, createBoxStyle 함수가 새로 정의된 경우에만 렌더링 하도록 한다.

함수가 새로 정의되었다는 것은 즉, 상자의 사이즈가 변경되었다는 것을 뜻한다. 

 

여기까지는 문제가 없으나, App 컴포넌트에 새로운 state가 추가된다면 ?!!

 

 

 

 

이렇게 만약 배경 색상을 변경하는 버튼이 하나 추가되었어도, 박스키우기는 계속해서 렌더링 된다. 

따라서, createBoxStyle이 렌더링되면서 계속해서 새로 생성되기 때문이다. 

따라서 여기에 useCallback함수를 넣어주면 렌더링에서 제외할 수 있다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

React.memo
컴포넌트를 인자로 전달하고, 해당 '컴포넌트'를 memoization 한다! 

 

 

Student 인자를 갖는 School 컴포넌트가 있다고 가정, 

React.memo를 이용하여 작성하면, 해당하는 컴포넌트를 propcheck를 통해서 렌더링하는 것이 아닌 재사용 하는 것이다! 

 

 

무분별하게 사용하면 오히려 성능에 독이 될 수 있다!  (데이터를 어딘가에 저장해야하기 때문에)

 

 

 

 

 

1. 부모, 자식 컴포넌트가 서로 다르고, 나이를 증가하는 프로그램

부모 컴포넌트가 존재하고 , 자식 컴포넌트에 props를 넘겨주는 형태의 프로그램이다. 

이 프로그램은 결국 App에 있는 부분을 렌더링하기 때문에, 부모의 나이값이 변화하면 

자식도 함께 리렌더링된다. 

 

따라서,효율적으로 하기 위해서는 '자식컴포넌트' 에 memo 를 씌워주는 방법이 있다. 

자식의 나이값이 바뀌지 않는 한 리렌더링 되지 않는 것이다. 

 

 

간단하게 export 에 memo를 씌워주어도 동작한다. 

 

어떻게 동작하는거지?  

=> 자식 컴포넌트가 렌더링이 될 상황에 놓일 때마다, 'prop-check'를 통해 최적화를 해주게 된다. 

child 의 props 인 {name, age}! 이 값에 변화가 없다면, 자동적으로 렌더링 되지 않는 것이다. 

 

 

만약 자식 컴포넌트에 전달되는 props가 객체나 함수라면... 주소값을 참조하고 있으므로 이야기가 달라진다! 

이 경우에는 , 

 

 

 

 

 

custom Hook