대부분의 최적화는 Props의 변경을 줄이는 타겟팅한다. 불변객체(immutable.js 등)을 사용하거나, useMemo, useCallback을 사용하는 식이다. 하지만 그것보다 먼저 고려해야하는 것이 부모의 컴포넌트의 리렌더링을 줄이는 것이다.
클래스 컴포넌트 라이프 사이클 API shouldComponentUpdate
는 렌더링 여부를 결정합니다. 렌더링 직전에 실행되어서 false을 리턴하면 렌더링 되지 않고 true이면 렌더링 됩니다.
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;
함수 컴포넌트에서 PureComponent와 동일한 기능을 하는 게 있을까요? state는 없고 props는 있습니다. 함수 컴포넌트에서 state는 useState의 setState를 통한 상태 변경은 단순 비교만 진행합니다. 원시 값은 값 비교, 객체는 참조 비교를 합니다. 그래서 state쪽은 PureComponent를 대체할 라이브러리는 없습니다. 반면, props는 React.memo로 PureComponent와 동일한 기능을 수행합니다.
참고 : 공식문서
바로 참조값을 비교하지않고 얇은 수준(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을 참고하시기 바랍니다.