티스토리 뷰

글쓰다가 한번 쫙 다 날려서 욕한번 거하게 날리고 싶어요 티스토리.. 글 수정할때도 임시저장되게 해주세요..

React.memo() //불필요한 리렌더(re-render)를 막아주는 함수

React.memo는 고차 컴포넌트(Higher Order Component)입니다. 컴포넌트가 동일한 props로 동일한 결과를 렌더링한다면 React.memo를 호출하고 결과를 메모라이징하도록 래핑하여 경우에 따라 성능 향상을 할 수 있다. 

즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용한다. 

->

리액트 룰에 따르면 부모 컴포넌트에서 state가 바뀌면 자식 컴포넌트들이 전부 리렌더링된다. 이렇게 되면 실제로 state가 변경된 컴포넌트는 하나일 뿐인데 나머지 자식 컴포넌트들도 리렌더되어 성능영향을 미칠 수 있다. 

같은 컴포넌트를 공유하는 다른 prop값을 가진 커스텀 컴포넌트가 있다면 그 중에서도 변경되는 props(props가 state와 연결되어 있기 때문에)에 해당하는 아이만 리렌더하고 값이 변동되지 않는 컴포넌트는 다시 렌더링하지 않게 기억해두는 것이 바로 memo함수이다.

 

<script type="text/babel">
    function Btn({ text, changeValue }) {
      console.log(text, "was rendered");
      return (
        <button
          onClick={changeValue}
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            marginRight: "5px",
            // fontSize: big ? "12px" : "10px",
          }}
        >
          {text}
        </button>
      );
    }
    const MemorizedBtn = React.memo(Btn);
    function App() {
      const [value, setValue] = React.useState("Save Changes");
      const changeValue = () => {
        setValue("Revert Changes");
      };
      return (
        <div>
          <MemorizedBtn text={value} changeValue={changeValue} />
          <MemorizedBtn text="Continue" />
        </div>
      );
    }
    const root = document.getElementById("root");
    ReactDOM.render(<App />, root);
  </script>

 

  • 버튼을 클릭하면 내 어플리케이션으로부터 온 function changeValue가 불러와지고 value가 재설정된다. 
  • value가 바뀔 때 리렌더를 확인할 수 있다.(데이터를 수정하는 함수가 불려질때 리렌더 되기 때문에) ->결과물: Save Changes -> Revert Changes
  • 부모 컴포넌트인 App은 state변경을 겪고 그 상태가 변경될 때 return에 있는 모든 것이 리렌더링된다.
  •  

해당 예시코드에서 부모 컴포넌트인 App에서 Btn이라는 컴포넌트가 2개 존재하고 각각은 다른 props값을 가지고 있다. 위의 버튼 컴포넌트는 value라는 state값을 가지고 있고 해당 state는 부모 컴포에서 버튼 클릭 시, 상태가 변하게 된다.(초기값 Save Changes 에서 Revert Changes 로) 자식 컴포들이 전부 리렌더 된다는 뜻인데 이것을 막아주기 위해서 const MemorizedBtn = React.memo(Btn); 을 사용한다. 처음 화면이 떴을 때 두 버튼이 렌더링되고 이것을 기억해두었다가 전체를 리렌더해야하는 상황(부모 컴포인 App이 state변화를 겪음)이 오면 props(state)가 변경된 컴포넌트만 리렌더하게 할 수 있다. 

 

 

Props 부가설명

위의 코드를 보면 prop의 값으로 함수가 들어갈 수 있다. changeValue={changeValue} 가 해당 부분이다. 

-> 이것은 JSX로 html태그 자체에 이벤트리스터를 넣는 것과는 전혀 다른 것이다. 

-> 이벤트를 실행시키는 함수가 프로퍼티로 들어간 것으로 prop은 그냥 부모에서 자식으로 데이터를 넘길 때 사용하는 argument의 역할을 한다고 생각하면 된다. 즉, prop이 onClick = {changeValue} 로 들어가 있다고해서 이벤트 리스너가 아니라 그냥 prop명이 onClick인 것이고 실제로 적용시켜주려면 자식 컴포넌트의 내부에 이벤트 리스너를 달고 그곳에 부모로부터 받은 prop명을 써주면 된다. 

 

&amp;amp;amp;amp;amp;amp;amp;nbsp;


Prop Types

props객체는 원하는 어떤 prop이든 보낼 수 있게 해준다(text, boolean 함수 등등). 다 컴포넌트의 환경을 우리가 원하는 만큼 설정할 수 있기 때문이다. 

하지만 리액트는 props의 type을 체크해주지 못한다. 그렇게 되면 우리가 prop의 값으로 잘못된 데이터 값을 전달한다해도 오류를 감지하지 못하고 원하는 모습으로 작동하지 않는 불상사가 생긴다(에러가 뜨지 않는다. prop의 값으로는 여러가지가 올 수 있으니).

  • 위의 예시에서 prop의 text의 값은 String으로 와야하고 ftSize는 number로 와야하는데 실수로 반대로 적었다고 하자. 코드는 정상작동하기 때문에 컴포넌트가 많을 경우 알아채지 못할 수 있다.  이를 해결하기 위해 어떤 패키지를 가져와서 사용해보자.

PropTypes

  • prop의 타입을 설정하고 체크할 수 있는 기능을 제공합니다. 오류역시 콘솔을 통해 알 수 있습니다. 
  • 해당 라이브러리가 다른 패키지로 이동되어 있기 때문에 cdn을 이용해야합니다.
  • CDN을 제공하는 사이트가 cdnjs와 unpkg 두 가지가 있는데 통상적으로는 cdnjs를 더 이용한다고 나와있으나 간단한 예시에 적용할 거라서 그냥 위에있는 unpkg CDN을 사용한 거 같네요.
    <!--prop types unpkg dev version-->
    <script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
    해당 코드를 넣어주면 PropTypes 라는 객체를 가질 수 있는데 이 객체가 바로 prop의 타입을 정의해주고 오류를 알려주는 역할을 한다. 
 

Typechecking With PropTypes – React

A JavaScript library for building user interfaces

reactjs.org

위의 링크를 타고 리액트 공식 홈페이지를 가보면 더 상세하게 어떤 타입들을 제어할 수 있는지 예시로 보여주고 있다.

  • array , bool(ean), func(tion), number, object, string, symbol, node 등등

 

▶Prop Types 적용해보기

 <body>
    <div id="root"></div>
  </body>
  <!-- react and reactDOM -->
  <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
  <!--prop types-->
  <script src="https://unpkg.com/prop-types@15.7.2/prop-types.js"></script>
  <!-- Load Babel -->
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  <script type="text/babel">
  
  //정의되지 않은 변수에 관한 기본값을 줄 수 있다. React가 아니라 JS가 제공하는 기능
  //ftSize = 16으로 기본값을 줬고 2번째 버튼은 ftSize가 prop으로 들어가있지 않아서 16px으로 지정됨
    function Btn({ text, ftSize = 16 }) {
      return (
        <button
          style={{
            backgroundColor: "tomato",
            color: "white",
            padding: "10px 20px",
            border: 0,
            borderRadius: 10,
            marginRight: "5px",
            fontSize: ftSize,
          }}
        >
          {text}
        </button>
      );
    }

    // const MemorizedBtn = React.memo(Btn);
    Btn.propTypes = {
      text: PropTypes.string.isRequired,
      fontSize: PropTypes.number,
    };
    function App() {
      return (
        <div>
          <Btn ftSize={18} />
          <Btn text="Continue" />
        </div>
      );
    }
    const root = document.getElementById("root");
    ReactDOM.render(<App />, root);
  </script>
  • Btn의 매개변수에 정의되지 않은 변수에 관한 기본값을 줄 수 있다. React가 아니라 JS가 제공하는 기능
  • 해당코드는 text 프로퍼티를 필수항목으로 지정하고 있기 때문에 첫번째 버튼에 text프로퍼티가 없어서 에러메세지를 콘솔에 표시한다. 

▶Prop Type 사용 시 주의사항

  • 해당 예제에서는 React.memo()때문에 실행이 안될 수도 있음 -> React.memo코드 아래에 작성하면 정상 가동
  • prop type이 제대로 설치됐는 지 확인하려면 콘솔에 PropTypes 쳐보기 -> 객체가 뜬다면 성공
  • prop types를 설치했는데도 오류 메세지가 나오지 않는다면 React의 URL(버전)을 production.min -> development로 변경

▶PropType 코드 작성법

  • 부모컴포넌트명.propTypes = { prop명 : PropType.타입형식명.(isRequired), 다른prop명 ~ ~ }
    •  (isRequired)에서 소괄호는 optional(선택사항)을 표현한 것으로 isRequired를 쓸 경우 소괄호는 제거하고 작성합니다. 필수로 입력해야하는 프로퍼티를 지정해주는 것으로 만일 해당 프로퍼티가 존재하지 않을 경우 콘솔에 오류 메세지가 뜹니다.
댓글