[TIL] React Hook React.memo란?

gun·2021년 4월 9일
2

TIL

목록 보기
18/19

React.memo

바로 이전 포스팅에서 useCallback에 대해 알아봤습니다.

이번 포스팅에서는 컴포넌트의 리 렌더링을 방지해 최적화를 시켜보겠습니다.

useCallback은 최적화된 자식 컴포넌트에 콜백 함수를 내려줄 때 유리하다. 라고 하는데 정확히 어떻게 작동하는지 React.memo와 함께 알아보겠습니다.

React 리렌더링을 방지하기 위해 사용되는 React.memo 함수에 대해 알아보겟습니다.

React.memo는 언제 사용하나요?

React.memo를 사용하면, 컴포넌트가 조건에 충족됐을 때 리렌더링 하더록 설정해 줄수있습니다.
여기서 말하는 조건이란 컴포넌트의 props의 변경 여부를 말합니다.

React.memo는 고차 컴포넌트(Higher Order Component)입니다.
Reat.memo 문서

고차 컴포넌트 HOC이란?

고차 컴포넌트(HOC, Higher Order Component)는 컴포넌트 로직을 재사용하기 위한 React의 고급 기술입니다. 고차 컴포넌트(HOC)는 React API의 일부가 아니며, 리액트의 구성적 특성에서 나오는 패턴입니다.
구체적으로, 고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수입니다.
React HOC 문서

React.memo에 대해 React 공식문서는 다음과 같이 설명합니다.

당신의 컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.
React.memo는 props 변화에만 영향을 줍니다. React.memo로 감싸진 함수 컴포넌트 구현에 useState, useReducer 또는 useContext 훅을 사용한다면, 여전히 state나 context가 변할 때 다시 렌더링됩니다.

다시말해, 부모 컴포넌트가 리 렌더링 되어도, 자식 컴포넌트에 같은 값, 즉 props가 이전과 동일한 결과를 렌더링한다면, React.memo를 호출하고 컴포넌트를 메모이징(Memoizing) 하도록 래핑하여 결과를 보여주게 됩니다.

즉 React는 컴포넌트를 리 렌더링 하지 않고 마지막으로 렌더링된 컴포넌트를 재 사용하게 됩니다.

한번 직접 사용해 보겠습니다!

React.memo 사용

이전 포스팅에서 useCallback을 사용하여 deps의 값이 바뀌지 않으면 함수를 재생성 하지 않도록 작성 했습니다.

React.memo와 어떤 시너지를 낼지 한번 보겠습니다!

다음과 같이 코드를 수정해주세요!

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

const CounterButton = React.memo(function ({ onClicks, count }) {
  console.log("카운터 버튼 렌더링");
  return <button onClick={onClicks}>{count.num}</button>;
})

export default function Counter() {
  const [count1, setCount1] = useState({ num: 0 });

  const increament1 = useCallback(() => {
    setCount1({ num: count1.num + 1 });
  },[count1]);

  const [count2, setCount2] = useState({ num: 0 });

  const increament2 = useCallback(() => {
    setCount2({ num: count2.num + 1 });
  },[count2]);

  return (
    <div className="App">
      <div>{count1.num}</div>
      <div>{count2.num}</div>
      <CounterButton onClicks={increament1} count={count1} />
      <CounterButton onClicks={increament2} count={count2} />
    </div>
  );
}

위 처럼 CounterButton 컴포넌트를 React.memo함수에 감싸주시면 사용이 완료됩니다!

한번 버튼을 클릭 해보시겠어요?

두번씩 출력되던 "카운터 버튼 렌더링"이 한번만 뜨게 됩니다! 불필요한 리렌더링이 되지 않습니다!

이제 React.memo가 어떤일을 하시는지 아시겠나요? increament1 버튼을 클릭하게 되면, React.memo가 이전값과 현재 props를 비교해 값이 같다면 마지막으로 렌더링된 결과를 재사용합니다.

그럼 useCallback은 필요 없는거 아닌가...?

useCallback, React.memo를 같이 사용하는 이유

React 공식문서에서 React.memo는 얕은 비교를 수행한다고 설명하고 있습니다.

props가 갖는 복잡한 객체에 대하여 얕은 비교만을 수행하는 것이 기본 동작입니다.

이게 useCallback을 사용하는것과 무슨 상관이 있어?!

한번 useCallback을 사용하지 않고, 일반 함수로 작성 해보고 클릭하면 결과가 어떻게 나오는지 해보셨음 좋겠습니다.

결과는 console에 "카운터 버튼 렌더링"이 2번씩 뜨게됩니다.
뭐야? React.memo를 사용했으니 하나는 리 렌더링이 안돼야 하는거 아니야?!

맞습니다. 다시한번 useCallback을 살펴보시면,

useCallback은 조건에 따라 함수를 재 생성 하거나, 재 사용한다.

CounterButton의 count 는 같은 값이지만, onClick을 다른 값으로 판단해 리 렌더링 발생 합니다.

스크롤을 올려 react.memo가 어떤 비교를 수행한다고 했는지 다시한번 봐주시길 바랍니다.

React.memo는 복잡한 객체에 대하여 얕은비교를 한다고 설명하고 있습니다.

얕은 비교란 쉽게말해 같은 주소를 참조하고 있냐? 라는 말 입니다.

자바스크립트에서 함수를 객체로 취급하기 때문에 useCallback을 사용하지 않은 increament 함수는 재 생성이 되고, 함수의 주소값이 다르기 때문에 React.memo는 다른 값을 참조하고 있다고 판단해 리 렌더링을 발생시킵니다.

이제 왜 함께 사용하는지 아시겠나요??

React.memo는 얕은 비교를 한다고 했는데 얕은 비교로는 비교할 수 없을 때 방법을 설명하겠습니다.

얕은 비교가 아닌 커스텀을 해 사용하고 싶다면, React.memo의 두 번째 인자로 비교 함수를 넣어주면 됩니다.

function Equal(prevProps, nextProps) {
  if (prevProps.count === nextProps.count) return true;
  return false;
}

const CounterButton = React.memo(function ({ onClicks, count }) {
  console.log("카운터 렌더링");
  return <button onClick={onClicks}>{count.num}</button>;
}, Equal);

... function Counter() {}...

다음과 같이 객체를 커스텀해 비교가 가능합니다.
Equal함수의 리턴 값이 ture면 같은 값으로, 리 렌더링을 방지하고, false면 리 렌더링을 발생시킵니다.

마치며

평소에 React 최적화를 작업하며 자주 사용했던 함수들이 어떤 동작을 하는지 대충 알고 있었지 정확히 알지 못했는데 이번 기회를 통해 내부적으로 어떤 동작을 하는지 알게 되는 좋은 시간 이었습니다.

새로운 기술을 도입하며 무슨 동작이 일어나나 정확히 알고 진행하자고 스스로 말하면서 지키지 못했던것 같습니다...

reference

https://velog.io/@yejinh/useCallback%EA%B3%BC-React.Memo%EC%9D%84-%ED%86%B5%ED%95%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94

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

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

0개의 댓글