2 분 소요

📌 리액트 공식 문서 https://ko.reactjs.org/docs/getting-started.html -> API 참고서 -> react -> React.memo

🔮 React.memo의 역할

const MyComponent = React.memo(function MyComponent(props) {
  /* props를 사용하여 렌더링 */
});

공식 문서에 나와 있는 예시인데, 설명을 읽어보면 컴포넌트(MyComponent)를 React.memo 함수의 매개변수로 전달하면 성능이 향상된 컴포넌트(고차 컴포넌트)로 반환 해준다.

-> 고차 컴포넌트가 된 MyComponent는 props가 변하지 않는다면 리랜더되지 않는다. (물론 props가 변하면 리랜더가 되겠지만,,,)

📐 OptimizeTest.js

이 컴포넌트는 그냥 React.memo()의 기능을 확인하기 위해서 만든 컴포넌트이다.

import { useState, useEffect } from "react";

const Countview = ({ count }) => {
  useEffect(() => {
    console.log(`Update :: count : ${count}`);
  });
  return <div>{count}</div>;
};

const Textview = ({ text }) => {
  useEffect(() => {
    console.log(`Update :: Text : ${text}`);
  });
  return <div>{text}</div>;
};

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [text, setText] = useState("");
  return (
    <div style=>
      <div>
        <h2>count</h2>
        <Countview count={count} />
        <button onClick={() => setCount(count + 1)}>+</button>
      </div>
      <div>
        <h2>text</h2>
        <Textview text={text} />
        <input value={text} onChange={(e) => setText(e.target.value)} />
      </div>
    </div>
  );
};

export default OptimizeTest;
  • OptimizeTest 컴포넌트 밑에
    • Countview 컴포넌트와
    • Textview 컴포넌트가 존재

Countview와 Textview 컴포넌트가 변경될 때마다 업데이트를 해주기 위해서 useEffect() 사용

❓ 문제점 ❓
근데 이렇게 되면 컴포넌트가 변경될 때마다 업데이트를 해주긴 하는데, 변경되지 않은 나머지 컴포넌트도 반드시 업데이트를 하게 된다.

💡 해결책 💡
React.memo() !

📐 React.memo() 사용 1

const Countview = React.memo(({ count }) => {
  useEffect(() => {
    console.log(`Update :: count : ${count}`);
  });
  return <div>{count}</div>;
});

const Textview = React.memo(({ text }) => {
  useEffect(() => {
    console.log(`Update :: Text : ${text}`);
  });
  return <div>{text}</div>;
});

-> 이렇게 되면 변경되지 않은 컴포넌트는 업데이트가 되지 않는 것을 확인할 수 있다.

📐 React.memo() 사용 2

두 번째 예시에서는 하나는 prop으로 그냥 숫자를, 다른 컴포넌트의 prop을 객체로 넣어보고 비교해본다.

const CounterA = React.memo(({ count }) => {
  useEffect(() => {
    console.log(`CounterA update :: count : ${count}`);
  });
  return <div>{count}</div>;
});

const CounterB = React.memo(({ obj }) => {
  useEffect(() => {
    console.log(`CounterB update :: obj.count : ${obj.count}`);
  });
  return <div>{obj.count}</div>;
});

지금 두 컴포넌트 모두 setCount()와 setObj()의 매개변수를 처음의 할당값인 1로 하고, 더이상 변화되지 않도록 count, obj.count만 전달한 상태 = 즉, 값이 변경되지 않는 상태라는 뜻

두 컴포넌트 모두 React.memo() 를 사용하였고, 값이 변경되지 않는다면 두 컴포넌트 모두 리랜더가 되지 않을 것 같다.

하지만 정수형을 전달했던 CounterA의 경우는 리랜더가 되지 않았지만, 객체를 전달했던 CounterB의 경우는 리랜더가 되고 있다. -> 왜 WHY 🖐️?

❗ 객체는 얕은 비교를 하기 때문 ❗

js는 객체, 함수, 배열은 주소를 통해서 비교하기 때문에 값이 같더라도 다른 주소이므로 다르다고 판단해서 onClick을 할 때마다 매번 리랜더가 되었던 것이다.

💡 해결책 💡
areEquel()을 사용

📍 areEquel()을 사용해서 깊은 비교해보기

areEquel() 같은 경우는
true를 반환하면 값이 같으므로 리랜더하지 말라는 뜻이고
false를 반환하면 값이 다르다는 의미이므로 리랜더하라는 뜻이다.

const areEquel = (prevProps, nextProps) => {
  if (prevProps.obj.count === nextProps.obj.count) {
    return true; // 이전 프롭스와 현재 프롭스가 같다 -> 리랜더링을 일으키지 않는다.
  }
  return false; // 이전 프롬스와 현재 프롭스가 같지 않다. -> 리랜더링을 해라
};

위 코드의 areEquel() 을 보게 되면 이전 객체의 count 값과 다음 객체의 count 값이 같으면(true) 리랜더하지 말고, 값이 다르면(false) 리랜더 하라는 의미이다. -> 이 함수의 리턴은 true/false

const MemoizedCounterB = React.memo(CounterB, areEquel);

이렇게 되면 컴포넌트는 CounterB가 아니라 MemoizedCounterB가 되므로 태그도 MemoizedCounterB로 변경 필요

태그:

카테고리:

업데이트:

댓글남기기