Today I Learned ... react.js
🙋♂️ React.js Lecture
🙋 My Dev Blog
// 일반 setState
setTries([...tries, { try: value, result: "홈런! 정답입니다." }]);
// 함수형 setState - ⭐ 이전 state 사용시
setTries(prevState => [
...prevState.tries,
{ try: value, result: "홈런! 정답입니다." },
]);
🔻 참고 - class형 Component
this.setState( {
tries: [
...prevState.tries,
{ try: value, result: "홈런! 정답입니다."}]
});
props 때문에 렌더링이 자주 일어나고,크롬 확장프로그램인
React devtool로 보면
Hooks로 쓴 컴포넌트는state의 이름이 나오지 않는다.
🙋♂️ 언제 render가 다시 되는지?
state나props가 바뀌었을 때.
devtool에서 ⚙ 버튼을 클릭하여
아래 부분에 체크를 해보자.
✅Highlight updates when components render

🚀 결과
Component가 리렌더링 될 때 마다하이라이트 된다.

input value만 바뀐건데
<Try />도 리렌더링 되고있음.
렌더링이 빠르게 될수록 빨간색에 가까워짐.
import React, { Component } from "react";
class TestRendering extends Component {
state = {
counter: 0,
};
onClickBtn = () => {
this.setState({});
};
render() {
console.log("렌더링" + this.state.counter);
return (
<div>
<button onClick={this.onClickBtn}>클릭</button>
</div>
);
}
}
export default TestRendering;
dev-tool로 확인해본 결과, highlight가 된다. 즉, 리렌더링이 된다!
React는 똑똑하지 않아서 (...)
즉, state나 props가 바뀌지 않아도 setState()만 호출해도 전체가 rendering이 된다.
🙋♂️ shouldComponentUpdate()를 사용하자!
shouldComponentUpdate는 Life-cycle Method 중 하나로, Class Component에서만 사용 가능하다.📌 shouldComponentUpdate(nextProps, nextState)
props를 변경하거나 setState()를 호출하여 state를 변경하면, 다시 렌더링해야하는지 판단하는 함수.
즉, 리렌더링 할지 말지 판단. 화면 변경을 위한 검증 작업. 데이터 변화를 비교하는 작업 포함됨.
(성능에 영향 多)
얕은 비교(shallow-compare)를 통해 데이터가 변경된 경우에만render()을 호출함.
shouldComponentUpdate(nextProps, nextState) {
if (this.state.counter !== nextState.counter) {
return true; // 값이 다름 - 변경된 것 -> 리렌더링 해라!
}
return false; // 값이 같음 - 변경 안됨 -> 리렌더링 하지마라
}
| shouldComponentUpdate()사용시 | 미사용시 |
|---|---|
![]() | ![]() |
| 렌더링되지 않는다! | 계속 렌더링 된다. |
이것이 바로 성능 최적화 하는것이다.
바뀌는 것이 없다면 렌더링 되지 않도록.
위에서 배운대로 일일히 state나 props가 바뀌면 true를 return하게 설정해줄 수 있지만,
PureComponent를 사용하면 편리하다.
🙋♂️ PureComponent란?
React.PureComponent 로 작성.React.Component를 상속받은 클래스.🔻 이전 예시 - PureComponent로 변경
import React, { Component, PureComponent } from "react";
class TestRendering extends PureComponent {
state = {
counter: 0,
};
onClickBtn = () => {
this.setState({});
};
render() {
console.log("렌더링" + this.state.counter);
return (
<div>
<button onClick={this.onClickBtn}>클릭</button>
</div>
);
}
}
export default TestRendering;
왜 PureComponent는
얕은 비교를 하는걸까?
- 값이 바뀌었는지 비교해야할 대상(props, state)이 만약 객체(Object)라면 값을 비교하기 어려워짐.
- 즉,
깊은 비교를 하면const list1 = [1,2,3]; const list2 = [1,2,3];위 코드에서
list1 === list2는 false이다.
- reference가 다르기 때문. 두 객체는 다른 객체임.
-> 이러면 값은 그대로인데도 (변화 X) 두 값은 일치하지 않으므로 렌더링이 발생함.반면에,
얕은 비교에선list1 === list2가 true가 된다.
두 값은 같기 때문에.
🙋♂️ 예제 변경
import React, { Component, PureComponent } from "react";
class TestRendering extends PureComponent {
state = {
array: [],
};
onClickBtn = () => {
this.setState({
array: [],
});
};
render() {
return (
<div>
<button onClick={this.onClickBtn}>클릭</button>
</div>
);
}
}
export default TestRendering;
array는 []에서 []로 바뀌었지만, 아예 새로운 객체로 선언되었기 때문에 리렌더링이 발생한다.
(원래 state인 []와 새롭게 변경한 값인 []는 ref, 즉 주소가 다르므로)
import React, { Component, PureComponent } from "react";
class TestRendering extends PureComponent {
state = {
array: [],
};
onClickBtn = () => {
const array = this.state.array;
array.push(1); // 원본이 변경됨
this.setState({
array: array, // 결국 변경되지 않아서, 리렌더링 X
});
};
// 생략
위 예제에서는 state인 array를 array의 원본에 push(1)을 하고난 후
setState()로 '변경된 array'로 바꾼다면?
-> PureComponent는 리렌더링을 하지 않음. (변경x)
spread(...)를 이용해줘야 한다!import React, { Component, PureComponent } from "react";
class TestRendering extends PureComponent {
state = {
array: [],
};
onClickBtn = () => {
this.setState({
array: [...this.state.array, 1], // 새로운 배열이 생성됨 -> state가 변경 -> 리렌더링 O
});
};
// 생략
참고 - 객체 안에 객체의 내부가 변경되어도 알아차리지 못할수도 있다. (원본이 같이 변경되는 경우는 변경 인지X)
따라서, 객체인 경우에는 새로운 객체를 만들어줘야함 (spread 이용)추가로, state = { {a: 1} } 에서 setState( { {a:1} }) 를 할때, 둘은 같지만
리렌더링 된다.
❌ state에 왠만해서는 (복잡한) 객체 구조를 쓰지 말자!
+) 지난시간 - 하위 컴포넌트로 분리 ()
// BullsAndCows.jsx의 tries.map 부분
<ul>
🔎 로그
{tries.map((v, index) => (
<Try key={`${index}차시도`} tryInfo={v} /> // v를 props로 넘겨줌
))}
</ul>
import React, { PureComponent } from "react";
class Try extends PureComponent {
render() {
const { tryInfo, index } = this.props;
return (
<li key={`${index}차시도`}>
{tryInfo.try} : {tryInfo.result}
</li>
);
}
}
export default Try;
memo 사용
Hooks 사용시
-> 함수이므로, 인수로 props를 받고, 디스트럭처링 할당.
// Try.jsx
import React, { memo } from "react"; // 👈 memo 임포트
const Try = memo(({ tryInfo, index }) => { // 👈 memo() 붙여주기
return (
<li key={`${index}차 시도:`}>
{tryInfo.try} : {tryInfo.result}
</li>
);
});
export default Try;
+) 부모 컴포넌트, 즉 BullsAndCows에도 memo를 해줄 수 있다.
import React from "react";
import { useState, useRef, memo } from "react"; // 👈 memo 임포트
import Try from "./Try";
const BullsAndCows = memo(() => { // 컴포넌트 전체에 memo() 씌워줌
// 생략
});
export default BullsAndCows;
✅ 결론
불필요한 리렌더링을 막아 반드시 성능 최적화를 해야한다!Class 컴포넌트일땐 -
PureComponent사용
Hooks 일땐 -memo()사용 (=React.memo)