Web/[JS] FrontEnd

sCU vs pureComponent vs React.memo

ihl 2021. 12. 16. 23:18

React

  React의 컴포넌트는 props 혹은 state가 변경될 때 해당 컴포넌트를 재 렌더링 한다. 그러나 경우에 따라선, 특정 props나 state가 변경되어도 컴포넌트를 재 렌더링 할 필요가 없을 경우도 있다. 이러한 케이스에서 재 렌더링을 효율적으로 수행하게 하는 방법들을 알아보자.

 

1. shouldComponentUpdate(sCU)

React LifeCycle

  shouldComponentUpdate(sCU)는 컴포넌트의 렌더링 여부를 결정하는 함수이다. 현재 props나 state 변화에 영향을 받아야 하는지 를 결정하는 함수라고 말할 수도 있다. 위 LifeCycle 그림에서 보듯이, props나 state 업데이트시, render 호출 직전에 sCU가 호출되며, sCU가 true를 리턴하면 render 함수가 호출되고, false를 리턴하면 render가 호출되지 않는다. sCU는 기본적으로 항상 true를 리턴한다.

 

shouldComponentUpdate(nextProps, nextState) {
    if(nextProps.id !== this.props.id) return true; // id가 변화했다!
    if(nextState.text !== this.state.text) return true; // text가 변화했다!
    
    return false;
}

  class형 컴포넌트 내에서 sCU를 위와 같이 재정의할 수 있다. 이렇게 정의한 컴포넌트는 props 혹은 state가 변화했다고 무조건 재렌더링되는 것이 아니라, id라는 props 혹은 text라는 state가 변경되었을 때만 재 렌더링(render() 호출)된다.

 

2. PureComponent

  React의 클래스 컴포넌트는 크게 React.Component와 React.PureComponent 2가지 이다. 이 둘의 차이는 sCU의 재정의 여부이다. PureComponent는 sCU가 props와  state에 대해 얕은 수준의(Shallow level) 비교를 수행하여 다른 값인 경우에만 재 렌더링 되도록 정의되어있는 Component다. 따라서 직접 sCU를 정의해야 하는 상황이 아닌 이상 React의 컴포넌트는 PureComponent를 상속받아 작성하는 것이 좋다. (더 정확히 이야기하자면, 구현하는 사람이 props가 얼마나 자주 변경되는지 파악하여 PureComponent와 Component 중 하나를 선택해야 한다. props가 잘 변경되지 않거나, 어느 정도 변경될 가능성이 있다면 pureComponent, props가 항상 변경된다면 Component가 낫다.)

 

  직접 sCU를 정의해야하는 상황은 무엇일까? 앞서 말했듯이 PureComponent는 sCU에서 얕은 수준의 비교를 수행하므로 Reference Type의 경우 PureComponent에서의 비교가 의미 없을 수 있다. 예를 들어 상위 컴포넌트에서 재 렌더링 시에 항상 화살표 함수를 선언하여 전달하는 경우, 같은 내용이지만 레퍼런스가 다르기 때문에 의도와는 다르게 하위 PureComponent도 재 렌더링 된다. 또한 상위 컴포넌트에서 직접 속성을 변경한 객체를 전달한 경우, 객체의 레퍼런스는 그대로이기 때문에 하위 PureComponent는 재 렌더링 되지 않을 수 있다. 이런 경우 sCU에 직접 깊은 비교를 수행하고 forceUpdate() 호출하는 등의 방식으로 원하는 바를 구현할 수 있다.

 

3. React.memo

  최근 React 생태계는 클래스 기반 컴포넌트 보단 함수 기반 컴포넌트를 작성하는 경우가 많다. 함수 기반 컴포넌트는 클래스 기반과 다르게 라이프 사이클 메서드를 사용할 수 없다. 그렇다면 함수 기반에서 props와 state 변화에 따른 재렌더링을 최적화할 수 있는 방법은 무엇일까? React 공식문서에서는 이 경우 React.memo라는 고차 컴포넌트(HOC) 사용을 권한다. (고차 컴포넌트란 컴포넌트를 이용하여 새로운 컴포넌트를 반환하는 함수이다. 이에 대한 설명은 추후 포스팅하겠다.)

 

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

  함수 기반 컴포넌트에 React.memo를 감싸는 방식으로 React.memo를 사용할 수 있다. React.memo를 통해 생성된 컴포넌트는 전달된 props 값의 변화에 따라 렌더링 여부를 결정한다. 상위 컴포넌트로부터 props를 전달받아 현재 갖고 있는 props와 얕은 수준의 비교를 수행하며, 수행 결과 다른 값이라면 재 렌더링 하고, 그렇지 않다면 재 렌더링 하지 않는다.

 

  React.memo는 PureComponent와 달리 state 변화에는 영향받지 않는다. state 변화는 useState 와 같은 훅을 이용하여 관리한다.

 

function MyComponent(props) {
  /* props를 사용하여 렌더링 */
}
function areEqual(prevProps, nextProps) {
   return prevProps.id === nextProps.id
}
export default React.memo(MyComponent, areEqual)

  sCU를 구현해야하는 경우는 위와 같이 React.memo의 두 번째 인자로 렌더링 여부를 결정하는 함수를 전달하면 된다. 위 경우 areEqual에서 id라는 props에 변화가 있는지 확인하고, 변화가 있다면 areEqual은 false를 리턴하고 컴포넌트는 재 렌더링 된다. 이때 sCU와 반대로 areEqual 함수는 props의 변화가 없으면 true를 반환한다는 점에 주의하자.

 

 


shouldComponentUpdate : https://reactjs.org/docs/react-component.html#shouldcomponentupdate

sCU in hook : https://ko.reactjs.org/docs/hooks-faq.html#how-do-i-implement-shouldcomponentupdate

pureComponent 와 Component 속도 : https://stackoverflow.com/questions/47611085/react-purecomponent-can-be-slower-than-component-how