네이버 블로그에서 이전, 원글 작성일시 : 2021.07.14.20:49
ZeroCho님의 강의를 보면서 React를 공부하고 있다.
강의를 바탕으로 내가 이해한 그대로 정리할 예정이다.
중대한 오류나 지식 부족이 발견될 확률이 높으니 혹시 이 글을 보는 사람이 있다면 주의하시길..
React 기본 강좌 3-11. PureComponent와 React.memo
chrome의 React Developer tools 확장 프로그램을 이용하면
React로 만들어진 페이지를 이것저것 분석해볼 수 있다.
그 중에서도 Highlight updates when components render 옵션을 이용하면
말그대로 어떤 컴포넌트들이 업데이트 되는지 실시간으로 확인할 수 있다.
이를 이용해 불필요한 렌더링을 하지 않도록 하는 방법에 대해 배웠다.
render() {
const {result, value, tries} = this.state;
return (
<>
<h1>{result}</h1>
<form onSubmit={this.onSubmitForm}>
<input ref={this.onRef} maxLength={4} value={value} onChange={this.onChangeInput} />
<button>입력!</button>
</form>
<div>시도: {tries.length}</div>
<ul>
{tries.map((v, i) => {
return (
<Try key={`${i+1}차 시도:`} tryInfo={v} />
);
})}
</ul>
</>
);
}
기본적으로 React에서 컴포넌트들은 state와 props가 업데이트, 즉 변경될 때 다시 렌더링 된다.
위의 예시는 강의에서 작성한 간단한 숫자야구 컴포넌트의 render() 메소드 내용이다.
숫자야구 컴포넌트는 value, result, answer, tries 를 state로 가지고,
하위 Try 컴포넌트에게 배열인 tries의 요소를 props로 전달하고 있다.
위 상황에서, 이벤트 메소드에 value, result만 변경하는 setState 메소드가 호출될때
Try 컴포넌트는 다시 렌더링 될까? 확인해보면 다시 렌더링 된다.
이처럼 실질적으로 사용하는 state/props는 바뀌지 않는데 다시 렌더링 되면 낭비라고 볼 수 있다.
이를 해결하기 위한 방법을 몇가지 정리해본다.
어떤 경우에는 다시 렌더링하고, 어떤 경우에는 그대로 두는 등 내맘대로 조절 할 수 있다.
return이 true이면 렌더링해라, false면 렌더링하지 말라는 뜻이다.
보통 현재 state/props와 nextProps/nextState를 비교해 렌더링 유무를 조절한다고 한다.
const React = require('react');
const { Component } = React;
class HoJangMan extends Component {
state = {
count: 0
};
shouldComponentUpdate(nextProps, nextState, nextContext) {
if(this.state.count === nextState.count) {
return true;
}
return false;
}
render() {
return ();
}
}
const React = require('react');
const {PureComponent} = React;
class HoJangMan extends PureComponent {
render() {
return ();
}
}
위와 같이 PureComponent를 상속하면 자동으로
현재와 과거의 state, props를 비교해서 렌더링 유무를 판별한다.
주의할 점은 객체나 배열처럼 참조를 이용하는 state/props들은 변경유무를 ===로 확인하기 때문에
값을 변경 시키고자 할때는 새로운 객체나 배열을 생성, 대입해줘야 한다.
(참조를 변경시켜야 한다는 뜻이다.)
특히 배열에 요소를 추가하는 경우 아래와 같이 작성하는 것을 권장한다.
this.setState({
arr: [...arr, 1] //끝에 요소 추가
});
함수형 컴포넌트에서는 PureComponent도 상속할 수 없고, shouldComponentUpdate도 사용할 수 없다.
이럴 때 사용하는 것이 바로 memo!
const React = require('react');
const { memo } = React;
const HoJangMan = memo(() => {
return ();
})
위 예시처럼 기존에 만들었던 컴포넌트 전체를 memo()로 감싸주기만 하면 끝!
추가적으로 알아두면 좋을 사항은 함수형 setState 이다.
state를 변경하고자 할 때, 이전 state를 이용해 현재 state를 변경할 예정이라면
아래와 같이 함수형으로 작성하는것을 권장한다.
this.setState((prevState, prevProps) => {
return {
count: prevState.count + 1,
}
});
setState에 객체를 인자로 줄 경우 비동기로 작동하기 때문에 순서를 보장하지 못하고,
순서가 필요한 로직의 경우 예상과는 다른 결과를 불러올 수 있다.
this.setState({ count: this.state.count+1 });
this.setState({ count: this.state.count+1 });
this.setState({ count: this.state.count+1 });
위와 같이 count를 1씩 올리는 setState 세번 호출 되고, 값을 확인해보면 +3 이아니라 +1 일 것이다.
자바스크립트에서 객체를 병합하거나 구성할 때, 중복된 key에 대해서는 마지막 값이 우선되기 때문이다.
하지만 함수형으로 작성하면, 해당 함수가 실행 큐에 순차적으로 쌓이게 되어 차례차례 실행 된다.
따라서 순서가 필요하거나, 이전 state/props를 이용하는 setState는 함수형으로 작성하도록 하자!