programming language/react

[React] React.memo, useCallback사용하기

공대키메라 2022. 6. 11. 17:44

지난 시간에는 React Hook 중에서 useContext, useRef, useMemo를 알아보았다.

(궁금하면 클릭!)

 

이번 시간에는 useMemo와 유사한 Reac.memo에 대해 알아보고 비교하는 시간을 가질 것이다.

 

그리고 useCallback을 통해서 또 한번 최적화를 배워 볼 것이다. 

 

참고 사이트는 다음과 같다. 

출처 : 

https://ko.reactjs.org/docs/react-api.html

https://react.vlpt.us/basic/19-React.memo.html

https://ko.reactjs.org/docs/hooks-reference.html#usecallback

https://www.daleseo.com/react-hooks-use-callback/

https://cocoon1787.tistory.com/798

1. React.memo 사용하기

지난 시간에 이어서 React.memo를 사용해서 좀 더 최적화를 할 것이다. 

 

지난 시간에 코드를 잠시 위치만 수정했다.

App.js

import React from "react";
import "./App.css";
import Clock from "./Clock";
import Count from "./Count";
import Header from "./Header";

export const UserContext = React.createContext();
function App() {
  let name = "키메라 끼에ㅔ엥ㄱ";
  let state = {
    result: "result",
    text: "text",
    name: "thelovemg",
  };
  return (
    <UserContext.Provider value={state}>
      <div className="App">
        <Header />
        <h2>
          <Clock />
          <Count />
        </h2>
        <header className="App-header">hello React! with... {name}</header>
      </div>
    </UserContext.Provider>
  );
}

export default App;

Header.js

import { useContext, useEffect, useRef, useState } from "react";
import { UserContext } from "./App";
import DataList from "./DataList";

const Header = () => {
  const [datas, setDatas] = useState([
    {
      name: "test1",
      seq: 123,
    },
    {
      name: "test2",
      seq: 122,
    },
    {
      name: "test4",
      seq: 124,
    },
  ]);

  const { text, name } = useContext(UserContext);

  const [resultStr, setResultStr] = useState("");
  const addTagContent = useRef("");

  useEffect(() => {
    addTagContent.current = resultStr;
  }, [resultStr]);

  return (
    <div>
      <div>
        <h1>{resultStr}</h1>
        <button
          onClick={() => {
            setResultStr((c) => c + "test");
          }}
        >
          add string to first tag... :
        </button>
        <DataList datas={datas} />
      </div>
      <br />
      header : {text} & {name}
    </div>
  );
};

export default Header;

 

이를 실행시키고 버튼을 클릭해서 내용을 추가하도록 하겠다.

 

 

필자는 "add string to first tag"  버튼을 클릭했는데 여러 곳에서 색이 변하는 것을 확인할 수 있다.

 

나는 버튼만 눌럿으면 버튼 위 태그에 내용만 추가되면 될 거 같은데 왜 DataList component가 rerendering 되는 것인가?

 

그렇다. App.js에 내용이 어찌되었든 변하는 상황이기 때문에 현재의 상황에서는 어찌되었든 내용이 변했으니 전부 다 그려버리는 것이다.

 

이렇게 되면 사소한 것 하나라도 수정이 일어난다면 하나의 컴포넌트에 포함된 모든 것이 rerendering되어버리니 성능에 악영향을 미칠 수 있다.

 

그렇기 때문에 React.memo를 이용해야 한다. 

 

React.memo를 이용해서 component를 재사용 하도록 하겠다. 

 

그럼 한 번 사용해 볼까? 휘리릭~!

 

사용법은 간단하다.

 

DataItem.js를 React.memo로 감쌀 것이다. 

DataItem.js

import React from "react";

const DataItem = React.memo(({ name, seq }) => {
  return (
    <div>
      {name} and {seq}
    </div>
  );
});

export default DataItem;

 

 

이렇게만 해주면 DataItem component를 다시 재사용함으로서 rendering이 덜 일어남을 확인할 수 있다.

2. useCallback 사용하기

useCallback은 memoization된 콜백을 반환하는 hook이다. 

 

즉, 이것도 useMemo와 마찬가지로 성능 최적화를 위해 사용되는 hook 중 하나이다. 

 

https://ko.reactjs.org/docs/hooks-reference.html#usecallback

 

이를 위해 좋은 예시가 있어서 내 연습장에 추가했다.

 

예시를 보기 전에 잠깐! 다시 한 번 더 뜻을 되새김질 해보자.

callback이 무엇인가? 특정 함수들을 후의 처리로 넘기는 것이다. 어떤 작업이 벌어진 뒤에, 함수를 부른다는 것이다. 

너를 다시 나중에 부를게! 콜백!

그러면 useCallback은? react에서 callback기능을 사용할 수 있게 지원하는 것일 뿐이다.

App.js - 구조만 정리

import React from "react";
import "./App.css";
import Clock from "./Clock";
import Count from "./Count";
import Header from "./Header";

export const UserContext = React.createContext();
function App() {
  let state = {
    result: "result",
    text: "text",
    name: "thelovemg",
  };
  return (
    <UserContext.Provider value={state}>
      <div className="App">
        <Header />
        <Clock />
        <Count />
        <br />
      </div>
    </UserContext.Provider>
  );
}

export default App;

Header.js - Light component 추가

import { useCallback, useContext, useEffect, useRef, useState } from "react";
import { UserContext } from "./App";
import DataList from "./DataList";
import Light from "./Light";

const Header = () => {
  const [datas, setDatas] = useState([
    {
      name: "test1",
      seq: 123,
    },
    {
      name: "test2",
      seq: 122,
    },
    {
      name: "test4",
      seq: 124,
    },
  ]);

  const { text, name } = useContext(UserContext);

  const [resultStr, setResultStr] = useState("");
  const addTagContent = useRef("");

  useEffect(() => {
    addTagContent.current = resultStr;
  }, [resultStr]);

  const [masterOn, setMasterOn] = useState(false);
  const [kitchenOn, setKitchenOn] = useState(false);
  const [bathOn, setBathOn] = useState(false);

  const toggleMaster = useCallback(() => {
    setMasterOn(!masterOn);
  }, [masterOn]);

  const toggleKitchen = useCallback(() => {
    setKitchenOn(!kitchenOn);
  }, [kitchenOn]);

  const toggleBath = useCallback(() => {
    setBathOn(!bathOn);
  }, [bathOn]);

  return (
    <div>
      <div>
        <h1>{resultStr}</h1>
        <button
          onClick={() => {
            setResultStr((c) => c + "test");
          }}
        >
          add string to first tag... :
        </button>
        <DataList datas={datas} />
        <br />
        <div>
          <Light room="침실" on={masterOn} toggle={toggleMaster}></Light>
          <Light room="주방" on={kitchenOn} toggle={toggleKitchen}></Light>
          <Light room="욕실" on={bathOn} toggle={toggleBath}></Light>
        </div>
      </div>
      <br />
      header : {text} & {name}
    </div>
  );
};

export default Header;

Light.js

import React from "react";

function Light({ room, on, toggle }) {
  console.log({ room, on });
  return (
    <div>
      <button onClick={toggle}>
        {room}
        {on ? "💡" : "⬛"}
      </button>
    </div>
  );
}

export default React.memo(Light);

 

그러면 버튼 클릭시 해당되는 Light component만 하이라이팅 되며 console도 하나만 나오는 것을 확인할 수 있다.

 

 

내가 직접 만들어서 하고싶은 욕심이 있었지만 이미 좋은 예시를 보고 말았으니 더이상 떠오르지가 않았다...ㅠㅠ

 

좋은 글 작성해주신 선배님 감사합니다...