React Hooks - useMemo, useCallback

jiny·2022년 9월 7일
0

React hooks

목록 보기
4/6
post-thumbnail

목차

  • 컴포넌트 렌더링 최적화
  • useCallback
  • useMemo

컴포넌트 렌더링 최적화

무분별한 컴포넌트의 렌더링을 방지하며 필요한 경우에만 컴포넌트가 렌더링될 수 있도록 하는 것

  • 리액트는 함수형 프로그래밍을 기반으로 한 자바스크립트 라이브러리
  • 그렇기 때문에 jsx 문법을 통해 컴포넌트를 만든 후 브라우저에 렌더링 하게 됨
  • 하지만 개발을 진행하다보면 무분별한 컴포넌트의 렌더링이 발생하게 됨

무분별한 컴포넌트의 재 렌더링

  • 의도하지 않은 곳에서 재 렌더링이 발생한 경우를 다음과 같이 표현
  • 예제를 통해 알아보자

app.tsx

import { useState } from "react";
import Box from "./component/Box";

function App() {
  const [size, setSizes] = useState(100);
  const [isDark, setIsDark] = useState(false);

  const createBoxStyle = () => {
    return {
      backgroundColor: "pink",
      width: `${size}px`,
      height: `${size}px`,
    };
  };
  return (
    <div style={{ background: isDark ? "black" : "white" }}>
      <input
        type="number"
        value={size}
        onChange={(e) => {
          setSizes(+e.currentTarget.value);
        }}
      />
      <button
        onClick={() => {
          setIsDark(!isDark);
        }}
      >
        Change Theme
      </button>
      <Box createBoxStyle={createBoxStyle} />
    </div>
  );
}

export default App;

  • input에서 size를 변경 하게 되면 createBoxStyle의 size값이 변경
  • 변경된 prop을 box 컴포넌트에 전달
  • 사용자가 change theme 버튼을 누르게 되면 배경이 검정색으로 변경

box.tsx

import { useEffect, useState } from "react";

type CreateBoxStyle = {
  backgroundColor: string;
  width: string;
  height: string;
};

interface BoxProps {
  createBoxStyle: () => CreateBoxStyle;
}

function Box({ createBoxStyle }: BoxProps) {
  const [style, setStyle] = useState({} as CreateBoxStyle);

  useEffect(() => {
    console.log("박스 사이즈 조절");
    setStyle(createBoxStyle());
  }, [createBoxStyle]);

  return <div style={style}></div>;
}

export default Box;
  • 전달 받은 createBoxStyle의 값이 변할 때마다 useEffect 실행
  • 박스 사이즈 조절 console에 출력 및 style을 setState 하여 div에 변경 된 style 적용

예상

  • 화살표를 클릭 하면 createBoxStyle의 size값이 변경되어 Box 컴포넌트에 전달
  • 전달 받은 Box 컴포넌트는 createBoxStyle의 크기가 변경되었기 때문에 useEffect 실행
  • change theme를 누르면 배경이 검정색으로 변경

하지만...

  • change theme를 눌러도 useEffect가 실행된 것을 확인

무분별한 재 렌더링이 발생한 이유

  1. change theme를 누르면 onClick 이벤트를 통해 setState 실행
  2. setState 함수로 state를 변경하면 컴포넌트 재 렌더링을 발생
  3. 재 렌더링이 발생하게 되면 컴포넌트(예시에선 app 컴포넌트)가 초기화
  4. 컴포넌트가 초기화되게 되면 createBoxStyle은 결국 js 함수이기 때문에 참조값(주소)가 변경되게 되고 변경된 참조값의 createBoxStyle이 prop으로 전달
  5. useEffect는 변수의 값이 달라졌다 생각하여 내부 코드를 실행
  • 모든 javascript 객체를 넣을 경우 참조하는 주소가 달라지기 때문에 재 렌더링 발생

해결 방법

React Hook에서 제공하는 useMemo, useCallback 사용

useCallback

  • 콜백 함수 자체를 메모리에 넣어 필요할 때마다 사용하도록 돕는 hook
  • 필요한 값을 Memoization 하여 컴포넌트 렌더링 최적화

Memoization

  • 자주 사용해야 하는 값을 받아오기 위해 반복적으로 계산해야 한다면 맨 처음 값을 계산할때 해당 값을 메모리에 저장하여 캐싱그 값이 필요할 때마다 캐시에서 가져와서 사용

useCallback의 구조

  • 첫 번째 인자 - callback function
  • 두 번째 인자 - dependency array(안에 넣은 변수의 값이 변할때마다 callback fn 호출)

예제 리팩토링

app.tsx

const createBoxStyle = useCallback(() => {
  return {
    backgroundColor: "pink",
    width: `${size}px`,
    height: `${size}px`,
  };
}, [size]);
  • createBoxStyle을 메모리에 저장 후 size의 값이 변경될 때만 캐싱된 createBoxStyle을 호출
  • size가 변경됬을 때만 Box 컴포넌트에 prop으로 전달

실행 결과

  • 3번이 아닌 2번만 출력된 것을 확인

useMemo

  • 콜백 함수의 return 값을 memoization 후 필요할 때마다 캐싱 된 return 값을 사용하도록 돕는 hook

  • useCallback과 마찬가지로 필요한 값을 Memoization 하여 컴포넌트 렌더링 최적화

useMemo의 구조

  • useCallback과 같은 구조

useMemo 예제

app.tsx

  const createBoxStyle = {
    backgroundColor: "pink",
    width: `${size}px`,
    height: `${size}px`,
  };

box.tsx

import { useEffect, useState } from "react";

type CreateBoxStyle = {
  backgroundColor: string;
  width: string;
  height: string;
};

interface BoxProps {
  createBoxStyle: CreateBoxStyle;
}

function Box({ createBoxStyle }: BoxProps) {
  const [style, setStyle] = useState({} as CreateBoxStyle);

  useEffect(() => {
    console.log("박스 사이즈 조절");
    setStyle(createBoxStyle);
  }, [createBoxStyle]);

  return <div style={style}></div>;
}

export default Box;
  • 전 예제에서 createBoxStyle을 객체로 변경
  • useCallback때와 마찬가지로 배경이 변경되면 Box 컴포넌트의 useEffect가 호출 될 것
  const createBoxStyle = useMemo(() => {
    return {
      backgroundColor: "pink",
      width: `${size}px`,
      height: `${size}px`,
    };
  }, [size]);
  • useMemo를 통해 size를 변경될 때마다 객체 안 property가 변경되도록 구성

실행 결과

  • 문제가 해결된 것을 확인

레퍼런스

별 코딩님 - React Hooks에 취한다 - useMemo 제대로 사용하기 | 리액트 훅스 시리즈

https://www.youtube.com/watch?v=e-CnI8Q5RY4&t=761s

별 코딩님- React Hooks에 취한다 - useCallback 제대로 사용하기 | 리액트 훅스 시리즈

https://www.youtube.com/watch?v=XfUF9qLa3mU&t=749s

0개의 댓글