TIL_81_230317

young0_0·2023년 3월 17일
0

TIL

목록 보기
78/91

81일

  • react 렌더링 최적화 memoization

memoization

컴퓨터가 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로서 동일한 계산을 하지 않도록 하여, 속도를 높이는 기술이다. 보통 애플리케이션의 최적화를 위해 사용된다.

  • React.memo (memo) : 컴포넌트 를 캐싱 (기억한다.)
  • useCallback : 함수를 캐싱
  • usememo : 값을 캐싱

리렌더링의 발생 조건

  1. state 바뀌었을 때
  2. props가 변경되었을 때
  3. 부모컴포넌트가 리렌더링 되었을때 자식컴포넌트도 리렌더링

memo(React.memo)

부모 컴포넌트가 리렌더링 되면 자식컴포넌트 모두 리렌더링 된다.

  • 부모 컴포넌트 변경시 자식 컴포넌트 렌더링은 불필요 하다.
export default function App() {
  console.log("App 컴포넌트 렌러링");

  const [count, setCount] = useState(0);

  const onClickPlus = () => {
    setCount(count + 1);
  };

  const onClickMinus = () => {
    setCount(count - 1);
  };

  return (
    <div className="App">
      <h3>카운트</h3>
      <p>현재 카운트 {count}</p>
      <button onClick={onClickPlus}>+1 </button>
      <button onClick={onClickMinus}>-1 </button>

      {/* 자식컴포넌트 */}
      <div style={boxesStyle}>
        <Box1 />
        <Box2 />
        <Box3 />
      </div>
    </div>
  );
}

  • 자식 컴포넌트에 React.memo로 감싸면 부모 컴포넌트가 변경 되어도 자식 컴포넌트는 리렌더링 되지 않는다.

function Box1() {
  console.log("Box1이 렌더링 되었습니다.");
  return <div style={boxStyle}>Box1</div>;
}

export default memo(Box1); //import 했을경우
/* 
 export default React.memo(Box1) //import 안했을 경우 
*/

useCallback

useCallback 은 인자로 들어오는 함수 자체를 기억(memoization)한다.

  • 프롭스가 변경 되었기 때문에 box1이 리렌더링 된다. (함수도 객체의 한 종류 이기 때문에 주솟값이 달라지고 하위 컴포넌트인 Box1.jsx의 props가 변경 되었다고 생각한다.)

-App.jsx

 console.log("App 컴포넌트 렌러링");

  const [count, setCount] = useState(0);

  //초기화 버튼
  const initCount = () => {
    setCount(0);
  };

  const onClickPlus = () => {
    setCount(count + 1);
  };

  const onClickMinus = () => {
    setCount(count - 1);
  };

  return (
    <div className="App">
      <h3>카운트</h3>
      <p>현재 카운트 {count}</p>
      <button onClick={onClickPlus}>+1 </button>
      <button onClick={onClickMinus}>-1 </button>

      {/* 자식컴포넌트 */}
      <div style={boxesStyle}>
        <Box1 initCount={initCount} />
        <Box2 />
        <Box3 />
      </div>
    </div>
  );
  • box1.jsx
function Box1({ initCount }) {
  console.log("Box1이 렌더링 되었습니다.");

  const onInitButton = () => {
    initCount();
  };

  return (
    <div style={boxStyle}>
      <button onClick={onInitButton}>초기화</button>
    </div>
  );
}

export default memo(Box1);

  • initCount 함수를 메모리 공간에 저장해 놓고 특정 조건이 아닌 경우에 변경되지 않도록 한다.
const onInitButton = useCallback(() => {
    initCount();
},[]);

  • useCallback 의존성배열에 아무것도 안들어있으면 초기에 렌더링 됬을 경우 만 저장 하고 안바뀌기 때문에 의존성 배열에 count (값)을 넣어 값이 변경 되었을 때 실행 되도록 수정 한다.
  const initCount = useuseCallback3Callback(() => {
    console.log(`count가 ${count}에서 0으로 변경되었습니다.`);
    setCount(0);
  }, [count]);

useMemo

값(객체, 배열, 함수)을 기억한다.
동일한 값을 반환하는 함수를 계속 호출해야 할때, 이전에 계산한 값을 메모리에 저장 함으로써 동일한 계산의 반복 수행을 제거 하여 프로그램 속도를 빠르게 한다.

  • 의존성 배열 안에 있는 값이 업데이트 될 때에만 콜백 함수를 다시 호출 하여 메모리에 저장된 값을 업데이트 한다.
import { useState } from "react";
 
const hardCalculate = (number) => {
  console.log("어려운 계산!");
  for (let i = 0; i < 99999999; i++) {} // 생각하는 시간
  return number + 10000;
};

const easyCalculate = (number) => {
  console.log("쉬운 계산!");
  return number + 1;
};

function App() {
  const [hardNumber, setHardNumber] = useState(1);
  const [easyNumber, setEasyNumber] = useState(1);

  const hardSum = useMemo(()=>hardCalculate(hardNumber),[]);
  const easySum = easyCalculate(easyNumber);

  return (
    <div>
      <h3>어려운 계산기</h3>
      <input
        type="number"
        value={hardNumber}
        onChange={(e) => setHardNumber(parseInt(e.target.value))}
      />
      <span> + 10000 = {hardSum}</span>
      
      
      <h3>쉬운 계산기</h3>
      <input
        type="number"
        value={easyNumber}
        onChange={(e) => setEasyNumber(parseInt(e.target.value))}
      />
      <span> + 1 = {easySum}</span>
    </div>
  );
}

export default App;
  • 쉬운계산을 눌러도 딜레이 시간이 걸린다. 이는 state값이 변경되면서 함수가 호출 되기 때문이다. 이를 막기 위해서 useMemo로 감싸 의존성 배열이 변경 될때 만 호출 되도록 하면 된다.

예제2

import { useMemo, useEffect, useState } from 'react'; 

function App() {
  const [number, setNumber] = useState(0);
  const [isKorea, setIsKorea] = useState(true);
  
  // const location = { country: isKorea ? '한국' : '일본' };
  
  
  const location = useMemo(() => {
    return {
      //객체 타입
      country: isKorea ? '한국' : '일본'
    }
  }, [isKorea])

  useEffect(() => {
    console.log('useEffect... 호출');
    // 뭔가 오래 걸리는 작업
  }, [location])

  return (
    <header className="App-header">
        <h2>하루에 몇 끼 먹어요?</h2>
        <input type="number" value={number} onChange={(e) => setNumber(e.target.value)}/>
        <hr/>

        <h2>어느 나라에 있어요?</h2>
        <p>나라: {location.country}</p>
        <button onClick={() => setIsKorea(!isKorea)}>Update</button>
    </header>
  );
}

export default App;
  • 객체 타입은 원시타입과 다르게 값이 저장될 때 주소 값으로 저장이 된다. 그래서 메모리 상의 주소가 다르게 저장 되므로 useEffect의 의존성 배열인 location 이 계속 변경된다. 따라서 useMemo를 사용 하여 여러번 호출을 막는다.

주의할점

useMemo를 남발하게 되면 별도의 메모리 확보가 너무나 많이 하게 되기 때문에 오리혀 성능이 악화될 수 있따. 필요할 때만 사용하는 것이 좋다.

profile
열심히 즐기자ㅏㅏㅏㅏㅏㅏㅏ😎

0개의 댓글