반응형
Notice
Recent Posts
Recent Comments
Link
관리 메뉴

짧은코딩

최적화2-컴포넌트 재사용 본문

인프런, 유데미/한입 크기로 잘라 먹는 리액트

최적화2-컴포넌트 재사용

5_hyun 2022. 5. 28. 22:34
반응형

App에서 state로 count, text가 있다.

그리고 자식 컴포넌트에 CountView는 count를 받고 TextView는 text를 받는다고 한다.

그럼 count가 변경되었을 때, 모두 자식도 모두 리렌더 된다. 하지만 TextView는 리렌더 될 필요가 없다.

=> 자식 컴포넌트에게 업데이트 조건으로 각각 count와 text가 변경될 때만 업데이트 되도록 조건을 걸면된다. 이것을 할 수 있는 것이 React.memo이다.

 

-리엑트 공식 문서

https://ko.reactjs.org/

 

React – 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리

A JavaScript library for building user interfaces

ko.reactjs.org

이 사이트를 잘 활용하여 리엑트를 배우면 좋다.

 

React.memo

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

 

React 최상위 API – React

A JavaScript library for building user interfaces

ko.reactjs.org

위 사이트에서 React.memo 설명이다.

 

-OptimizeTest.js

import React, { useState, useEffect } from "react";

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

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

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [text, setText] = useState("");

  return (
    <div style={{ padding: 50 }}>
      <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;

Textview, Countview는 Optimizetest의 자식 컴포넌트이다.

 

-출력 결과

하나의 상태를 바꿀 때, 둘 다 출력이되어 낭비가 된다.

 

-React.memo 사용

import React, { useState, useEffect } from "react";

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

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

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [text, setText] = useState("");

  return (
    <div style={{ padding: 50 }}>
      <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;

이렇게 자식 컴포넌트를 React.memo로 감싸면

 

이렇게 값이 바뀌면 둘 다 실행되지 않고 하나만 실행된다.

 


-다른 예시

import React, { useState, useEffect } from "react";

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 - count: ${obj.count}`);
  });

  return <div>{obj.count}</div>;
});

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [obj, setObj] = useState({
    count: 1,
  });

  return (
    <div style={{ padding: 50 }}>
      <div>
        <h2>Counter A</h2>
        <CounterA count={count} />
        <button onClick={() => setCount(count)}>A button</button>
      </div>
      <div>
        <h2>Counter B</h2>
        <CounterB obj={obj} />
        <button
          onClick={() =>
            setObj({
              count: obj.count,
            })
          }
        >
          B Button
        </button>
      </div>
    </div>
  );
};

export default OptimizeTest;

Counter A는 자기 자신으로 바꿔서 달라지는게 없다.

Counter B도 obj.count로 바꿔서 값은 그대로이다.

 

결과는 A 버튼을 누르면 콘솔에 아무것도 안찍힌다.

그치만 B 버튼은 누르면 콘솔에 B가 업데이트 되었다고 출력된다.

=> 결국 리렌더링이 일어나서 출력이 되는 것이다. 이유는 obj가 객체라서이다. 리엑트에서는 객체를 비교할 때 얕은 비교를 한다.

 

-얕은 비교

객체 a와 b를 만들고 프로퍼티에 count: 1을 넣었다.

근데 그리고 a와 b를 비교하면 같지 않다는 결과가 나온다.

왜냐면 객체는 만들어지면 교유의 메모리 주소가 생긴다. 

얕은 비교는 이 메모리 주소를 비교하기 때문에 같지 않다는 결과가 나온다.

 

하지만 a를 만들고 b에 a를 복사하고 비교하면 a와 b는 같다는 결과가 나온다.

 

-깊은 비교를 하도록 만드는 방법

const areEqual = (prevProps, nextProps)=>{
    return true //이전 프롭스 현재 프롭스와 같다 -> 리렌더링을 일으키지 않음
    return false //이전과 현재가 다르다 -> 리렌더링을 일으킨다
}

 

import React, { useState, useEffect } from "react";

const CounterA = React.memo(({ count }) => {
  useEffect(() => {
    console.log(`CounterA Update - count: ${count}`);
  });

  return <div>{count}</div>;
});

const CounterB = ({ obj }) => {
  useEffect(() => {
    console.log(`CounterB Update - count: ${obj.count}`);
  });

  return <div>{obj.count}</div>;
};

const areEqual = (prevProps, nextProps) => {
  return prevProps.obj.count === nextProps.obj.count;
};

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

const OptimizeTest = () => {
  const [count, setCount] = useState(1);
  const [obj, setObj] = useState({
    count: 1,
  });

  return (
    <div style={{ padding: 50 }}>
      <div>
        <h2>Counter A</h2>
        <CounterA count={count} />
        <button onClick={() => setCount(count)}>A button</button>
      </div>
      <div>
        <h2>Counter B</h2>
        <MemoizedCounterB obj={obj} />
        <button
          onClick={() =>
            setObj({
              count: obj.count,
            })
          }
        >
          B Button
        </button>
      </div>
    </div>
  );
};

export default OptimizeTest;

areEqual을 통해서 두 객체의 프로퍼티가 같은지 확인한다.

MemorizedCounterB는 2번째 인자에 따라서 리렌더링을 할지 안할지 결정한다.

그리고 MemorizedCounterB를 최종 리턴에 넣어주면 깊은 복사를 할 수 있다.

반응형
Comments