리렌더링

김준엽·2022년 6월 17일
0

React

목록 보기
8/11

리액트에서 컴포넌트가 리렌더링 되는 경우

  • Props가 변경되었을 때
  • State가 변경되었을 때
  • 부모 컴포넌트가 리렌더링되었을 때

대부분의 최적화는 Props의 변경을 줄이는 타겟팅한다. 불변객체(immutable.js 등)을 사용하거나, useMemo, useCallback을 사용하는 식이다. 하지만 그것보다 먼저 고려해야하는 것이 부모의 컴포넌트의 리렌더링을 줄이는 것이다.


shouldComponentUpdate

클래스 컴포넌트 라이프 사이클 API shouldComponentUpdate는 렌더링 여부를 결정합니다. 렌더링 직전에 실행되어서 false을 리턴하면 렌더링 되지 않고 true이면 렌더링 됩니다.


PureComponent

React.Component와 React.PureComponent는 클래스 컴포넌트에서 사용합니다.
React.Component는 shouldComponentUpdate을 따로 구현하지 않으면 항상 true를 반환합니다. this.setState가 실행되면 state, props 변경 여부를 신경 쓰지 않고 무조건 리렌더링합니다.

반면, React.PureComponent는 shallow compare를 하는 shouldComponentUpdate가 구현되어 있습니다. 그 compare 결과에 따라 리렌더링 여부를 결정합니다. 이러면 불필요한 렌더링을 줄일 수 있어 최적화에 도움됩니다.

아래 코드를 통해 확인해보시기 바랍니다.

import React, { Component, PureComponent  } from 'react';

class CounterC extends PureComponent  {
  state = {
    counter: 0,
  };

  handleIncrease = () => {
    // const tmp = this.state
    // tmp.counter++
    // console.log(tmp)
    // this.setState(tmp)

    this.setState((prev) => ({
      counter: 0,
    }))
  };

  // shouldComponentUpdate(newxProps, nextState, nextContext) {
  //   console.log(this.state)
  //   console.log(nextState)
  //   if (this.state.counter !== nextState.counter) {
  //     console.log(true);
  //     return true;
  //   }
  //   console.log(false임);
  //   return false; // false일 경우 렌더링되지 않는다.
  // }

  render() {
    return (
      <div>
        <h1>클래스 {this.state.counter}</h1>
        <button onClick={this.handleIncrease}>+1</button>
      </div>
    );
  }
}

export default CounterC;

함수 컴포넌트에서 PuerComponent

함수 컴포넌트에서 PureComponent와 동일한 기능을 하는 게 있을까요? state는 없고 props는 있습니다. 함수 컴포넌트에서 state는 useState의 setState를 통한 상태 변경은 단순 비교만 진행합니다. 원시 값은 값 비교, 객체는 참조 비교를 합니다. 그래서 state쪽은 PureComponent를 대체할 라이브러리는 없습니다. 반면, props는 React.memo로 PureComponent와 동일한 기능을 수행합니다.

참고 : 공식문서

shallow compare

바로 참조값을 비교하지않고 얇은 수준(depth 1)끼리 비교하는 것을 말합니다. 아래 코드를 통해 이해하기 바랍니다.

function shallowCompare(newObj, prevObj){
  for (const key in newObj){
      if(newObj[key] !== prevObj[key]) return true;
  }
  return false;
}

const game_item = {
  game: "football",
  first_world_cup: "1930",
  teams: {
       North_America: 1,
       South_America: 4,
       Europe: 8 
  }
}

// Case 1:
// if this be the object passed to setState
const updated_game_item1 = {
  game: "football",
  first_world_cup: "1930",
  teams: {
       North_America: 2,
       South_America: 4,
       Europe: 8 
  }
}
shallowCompare(updated_game_item2, game_item); // false - meaning the state
                                               // will not update.

// Case 2:
// if this be the object passed to setState
const updated_game_item2 = {
    game: "football",
    first_world_cup: "1930",
    teams: game_item.teams
}
shallowCompare(updated_game_item2, game_item); // false - meaning the state
                                               // will not update.

// Case 3:
// if this be the object passed to setState
const updated_game_item3 = {
    first_world_cup: 1930
}
shallowCompare(updated_game_item3, game_item); // true - will update, 엄격한 평가

shallow compare는 단순히 객체 참조값을 비교한다는 글을 심심치 않게 볼 수 있습니다. 다른 곳에서 모르겠으나 적어도 리액트에선 shallow compare 의미는 얕은 수준에서 비교하는 것을 말합니다. 그것에 대한 내용은 stackoverflow을 참고하시기 바랍니다.

profile
프론트엔드 개발자

0개의 댓글