[TIL 0425] Memoization

zitto·2023년 4월 25일
0

TIL

목록 보기
65/77
post-thumbnail

✅ 알아두면 유용한 개발자 도구

설치는 구글 웹스토어에서 진행

1. Apollo Client Devtools
→ 설치후 app.tsx에서 client 설정 부분에
connectToDevTools : true로 설정해야 한다.

2. wappalyzer
→특정 사이트에 들어가시면 해당 사이트가 사용한 스택을 분석해준다.

3. react developer tools 설치
: 눈으로 직접 확인하며 개발할 수 있는 도구
-> 개발자 도구에 proflier에 생성됨

렌더링 될 대상일 때 영역을 표시해주는 것 체크하기!

💡 성능최적화

import { useState } from "react";
export default function MemoizationPage(): JSX.Element {
  console.log("component 렌더링 됨");
  let countLet = 0;
  const [countState, setCountState] = useState(0);
  const onClickCountLet = (): void => {
    console.log(countLet);
    countLet += 1; //countLet = countLet + 1;
  };
  const onClickCountState = (): void => {
    console.log(countState);
    setCountState(countState + 1);
  };
  return (
    <>
      <div>COUNT(let) : {countLet} </div>
      <button onClick={onClickCountLet}>COUNT(let) + 1</button>
      <div>COUNT(state): {countState}</div>
      <button onClick={onClickCountState}>COUNT(state) + 1</button>
    </>
  );
}

let은 버튼을 누르면 콘솔의 값은 올라가지만 리렌더는 일어나지 않음!
따라서 "component 렌더링 됨" 이라는 콘솔이 찍히지 않고 있으며, 화면은 여전히 0 이다.

하지만 state는 버튼누름과 동시에 리렌더링되고, 올려두었던 countLet이 0으로 초기화 된다.

이는 useState를 제외한 모든 값이 다시 그려지고 있다는 것!!


자식컴포넌트를 분리 후 연결하면?

툴에서 확인해보면 렌더링 많이 되니 노란색으로 바뀜
부모의 state를 바꿨는데 자식도 다시 렌더링된다.

부모는 렌더링 되지만, 자식은 렌더링 하고 싶지 않을 땐?

✅ memo

import { memo } from "react";
function MemoizationWithChildPage(): JSX.Element {
  console.log("child 렌더링 됨");
  return (
    <>
      <div>======================</div>
      <h1>!!CHILD COMPONENT!!</h1>
      <div>======================</div>
    </>
  );
}
export default memo(MemoizationWithChildPage); //memo = HOC

state카운트를 클릭하면 컨테이너만 렌더링이 된다.

memo를 걸어두면 처음의 값이 기억되어 업데이트 되지 않는다.
memo로 적용된 컴포넌트는 상위 컴포넌트가 재렌더가 되더라도,
넘어오는 props 의 값이 동일하다면 컴포넌트를 재렌더하지 않게 된다.

부모컴포넌트에서도 부분적으로 렌더링이 일어나지 않아도 되는 부분이 있음!! => 규모가 커질수록 심해짐!


💡 Memoization(메모이제이션)

useMemo() & useCallback()

불필요한 값들이 지속적으로 다시 만들어지지 않도록 유지시켜주는 hooks

  • useMemo( 연산 함수, 적용할 변수를 배열로 감싸기)

import { useCallback, useMemo, useState } from "react";
import MemoizationWithChildPage from "./02-child";
export default function MemoizationPage(): JSX.Element {
  console.log("component 렌더링 됨");
  let countLet = 0;
  const [countState, setCountState] = useState(0);
  // 1. useMemo로 변수 기억
  const aaa = useMemo(() => Math.random(), []);
  console.log(aaa);

useEffect처럼 의존성 배열을 사용할 수도 있다.
props로 넘겨줘야하는 것은 꼭 필요한 것만 넘겨주기!

  • useCallback()

  // 2. useCallback으로 함수 기억
  // useCallback을 사용하게 되면 함수를 다시 만들지 않는다.
  const onClickCountLet = useCallback((): void => {
    console.log(countLet);
    countLet += 1; //countLet = countLet + 1;
  }, []);
  // 3. useCallback으로 함수 기억 => state사용주의
  const onClickCountState = useCallback((): void => {
    // console.log(countState);
    // setCountState(countState + 1);
    setCountState((prev) => prev + 1); //그때 그때 prev(기존값)를 가지고 와야함
  }, []);
  // 4. 잘못된 사용사례_useMemo로 나만의 useCallback 만들어보기
  // const onClickCountState2 = useMemo(
  //   () => (): void => {
  //     console.log(countState + 1);
  //     setCountState(countState + 1);
  //   },
  //   []
  // );
  //expected : 1111111111
  //state를 기억하기 때문에 아무리 count를 올려도 1만 찍히게 된다.(이전에 불러왔던 값을 유지) => prev를 이용해서 함수를 다시 불러오지 않고 값만 올려주기!
  return (
    <>
      <div>======================</div>
      <h1>!!PARENT COMPONENT!!</h1>
      <div>COUNT(let) : {countLet} </div>
      <button onClick={onClickCountLet}>COUNT(let) + 1</button>
      <div>COUNT(state): {countState}</div>
      <button onClick={onClickCountState}>COUNT(state) + 1</button>
      {/* <button onClick={onClickCountState2}>COUNT(state) + 1</button> */}
      <div>COUNT(state): {countState}</div>
      {/* 5. 잘못된 사용사례_로직과 UI가 합쳐져서 헷갈림 => 유지보수 힘듬, 메모이제이션 더 복잡!! */}
      {/* <button
        onClick={useCallback((): void => {
          setCountState((prev) => prev + 1);
        }, [])}

        COUNT(state) + 1
      </button> */}
      <div>======================</div>
      <MemoizationWithChildPage />
    </>
  );
}

다시실행되면 안되는게 메모
다시실행해주는게 콜백

❗️ useCallback을 쓰지 말아야 할 때

의존성 배열의 인자가 1~2개보다 많아질 때는 차라리 리렌더를 하는것이 유지 보수에는 더 좋은방법이다.
성능이 조금이나마 좋아지는 것 보다는 유지보수가 편리한 편이 훨씬 좋다.
따라서 의존성 배열의 인자가 2개를 초과할때는 그냥 리렌더를 해주시는게 좋음!!


✅ map과 memo의 관계

  • parent.tsx
import { v4 as uuidv4 } from "uuid";
import { useState } from "react";
import Word from "./02-child";
export default function MemoizationWithMapParentPage(): JSX.Element {
  const [data, setData] = useState("HI THERE");
  const onClickChange = (): void => {
    setData("BYE THERE");
  };
  return (
    <>
      {/* 2. memo해도 , key자체가 매번 변경되어 props로 넘어가므로, 5개 모두 리렌더링 됨 */}
      {data.split(" ").map((el) => (
        <Word key={uuidv4()} el={el} />
      ))}
      {/* 1. memo시 key 또는 el이 변경된 부분만 리렌더링 됨(즉, THERE은 제외! */}
      {/* {data.split(" ").map((el, index) => (
        <Word key={index} el={el} />
      ))}  */}
      <button onClick={onClickChange}>CHANGE</button>
    </>
  );
}
  • child.tsx
import { memo } from "react";
interface IWordProps {
  el: string;
}
function Word(props: IWordProps): JSX.Element {
  console.log("child 렌더링 됨", props.el);
  return <span>{props.el}</span>;
}
export default memo(Word);


▼ key값을 uuid로 설정시 문제점

uuid를 사용하면 memo를 걸어놔도 key값이 변경되어 props로 넘어가기 때문에 변경된 부분이 모두 리렌더링 된다.

uuid는 불필요한 리렌더링을 초래하므로 필요한 상황에서만 주의 해서 사용할 것!

profile
JUST DO WHATEVER

0개의 댓글