| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 구분
- 마사지
- Hooks
- 특수문자
- 중고거래사기
- 막탄
- 스쿠버다이빙
- ES6
- 유효성검사
- REACT
- Webpack
- webpack.config.js
- 정규식
- JavaScript
- 사기
- 중고나라사기
- js
- 맛사지
- plugin
- 스노쿨링
- 자동완성
- 세부
- 여행
- 네이버페이사기
- 해외여행
- 삼성무선청소기제트
- 중고나라
- autocomplate
- 정직하게사세요
- Today
- Total
Ryu.log
[ React-Tutorial-09 ] 불변성을 지키는 이유와 업데이트 최적화 본문
지난 섹션에서 배열을 어떻게 다뤄야 하는지에 대해서 알아보았다. 데이터를 업데이트하는 과정에서 불변성을 지켜야한다는 것을 강조 하였는데,
왜 그래야 하는지 알아보자.
01. 데이터 필터링 구현하기
// file: src/App.js
import React, { Component } from 'react';
import PhoneForm from './components/PhoneForm';
import PhoneInfoList from './components/PhoneInfoList';
class App extends Component {
id = 2
state = {
information: [
{
id: 0,
name: '유준호',
phone: '010-0000-0000'
},
{
id: 1,
name: '한승훈',
phone: '010-0000-0001'
}
],
keyword: ''
}
handleChange = (e) => {
this.setState({
keyword: e.target.value,
});
}
handleCreate = (data) => {
const { information } = this.state;
this.setState({
information: information.concat({ id: this.id++, ...data })
})
}
handleRemove = (id) => {
const { information } = this.state;
this.setState({
information: information.filter(info => info.id !== id)
})
}
handleUpdate = (id, data) => {
const { information } = this.state;
this.setState({
information: information.map(
info => id === info.id
? {...info, ...data}
: info
)
})
}
render() {
const { information, keyword } = this.state;
return (
<div>
<PhoneForm
onCreate={this.handleCreate}
/>
<p>
<input
placeholder="검색 할 이름을 입력하세요.."
onChange={this.handleChange}
value={keyword}
/>
</p>
<hr />
<PhoneInfoList
data={information}
onRemove={this.handleRemove}
onUpdate={this.handleUpdate}
/>
</div>
);
}
}
export default App;
검색어를 입력했을 때 필터링을 하는 것은 나중에 구현하도록 하자. 지금의 상황에선, input에 입력을 했을 때 업데이트가 필요한것은 오직 input 뿐이다.
하지만 App 컴포넌트의 상태가 업데이트 되면, 컴포넌트의 리렌더링이 발생하게 되고, 컴포넌트가 리렌더링이 되면 그 컴포넌트의 자식 컴포넌트도 리렌더링이 된다.
확인을 한번해보자 PhoneInfoList 컴포넌트에서 render 함수의 상단에 아래코드를 넣어보자.
// src/components/PhoneInfoList.js
...
render() {
console.log('render PhoneInfoList');
const { data, onRemove, onUpdate } = this.props;
const list = data.map(
info => (
<PhoneInfo
key={info.id}
info={info}
onRemove={onRemove}
onUpdate={onUpdate}
/>
)
);
return (
<div>
{list}
</div>
);
}
...이렇게 하고 검색어 input을 수정한 다음에 콘솔을 확인해보자.
App이 리렌더링됨에 따라 PhoneInfoList도 리렌더링이 되고 있다. 물론, 실제로 변화가 일어나진 않으니 지금은 Virtual DOM 에만 리렌더링한다.
지금의 상황에는 별로 큰 문제가 되지 않는데, 리스트 내부의 아이템이 몇백개, 몇천개가 된다면 이렇게 Virtual DOM에 렌더링 하는 자원은 아낄 수 있다면 아끼는게 좋다.
이러한 낭비되는 자원을 아끼기위해선 우리가 이전에 배웠던 shouldComponentUpdate LifeCycle API를 사용하면 된다.
자, PhoneInfoList 에서 shouldComponentUpdate를 구현해보자
그냥 단순히 다음 받아올 data가 현재 data랑 다른 배열일 때 true로 설정하게 하면 된다.
// src/components/PhoneInfoList.js
import React, { Component } from 'react';
import PhoneInfo from './PhoneInfo';
class PhoneInfoList extends Component {
static defaultProps = {
data: [],
onRemove: () => console.warn('onRemove not defined'),
onUpdate: () => console.warn('onUpdate not defined')
}
shouldComponentUpdate(nextProps, nextState) {
return nextProps.data !== this.props.data;
}
render() {
console.log('render PhoneInfoList');
const { data, onRemove, onUpdate } = this.props;
const list = data.map(
info => (
<PhoneInfo
key={info.id}
info={info}
onRemove={onRemove}
onUpdate={onUpdate}
/>
)
);
return (
<div>
{list}
</div>
);
}
}
export default PhoneInfoList;
이제 변화가 필요하지 않을 때는 render 함수가 호출되지 않게 된다.
우리는 shouldComponentUpdate 로직을 굉장히 간단하게 작성해주었는데 어떻게 이런게 가능한 것일까?
02. 불변성에 대해 알아보자
const array = [1,2,3,4]; const sameArray = array; sameArray.push(5); console.log(array !== sameArray); // false
우리가 sameArray = array를 했다고 해서 기존에 있던 배열이 복사되는 것이 아니라 똑같은 배열을 가르키고 있는 레퍼런스 하나가 만들어진 것이기 때문에,
우리가 sameArray 에 push를 하게 된다고 해서 array와 sameArray가 달라지지 않는다.
하지만, 우리가 불변성을 유지하면
const array = [1,2,3,4]; const differentArray = [...array, 5]; // 혹은 = array.concat(5) console.log(array === differentArray); // true
위 코드와 같이 바로바로 비교가 가능하다는 것이다.
이는 객체를 다룰때도 마찬가지이다.
// NO
const object = {
foo: 'hello',
bar: 'world'
};
const sameObject = object;
sameObject.baz = 'bye';
console.log(sameObject !== object); // false
// YES
const object = {
foo: 'hello',
bar: 'world'
};
const differentObject = {
...object,
baz: 'bye'
};
console.log(differentObject !== object); // true
03. 기능 마저 구현하기
구현하던 기능을 마저 끝내보겠다.
App컴포넌트에서 keyword 값에 따라서 information 배열을 필터링 해주는 로직을 작성하고, 필터링된 결과를 PhoneInfoList에 전달해 줘보자.
// file: src/App.js
import React, { Component } from 'react';
import PhoneForm from './components/PhoneForm';
import PhoneInfoList from './components/PhoneInfoList';
class App extends Component {
id = 2
state = {
information: [
{
id: 0,
name: '유준호',
phone: '010-0000-0000'
},
{
id: 1,
name: '한승훈',
phone: '010-0000-0001'
}
],
keyword: ''
}
handleChange = (e) => {
this.setState({
keyword: e.target.value,
});
}
handleCreate = (data) => {
const { information } = this.state;
this.setState({
information: information.concat({ id: this.id++, ...data })
})
}
handleRemove = (id) =>> {
const { information } = this.state;
this.setState({
information: information.filter(info => info.id !== id)
})
}
handleUpdate = (id, data) => {
const { information } = this.state;
this.setState({
information: information.map(
info => id === info.id
? {...info, ...data}
: info
)
})
}
render() {
const { information, keyword } = this.state;
const filteredList = information.filter(
info => info.name.indexOf(keyword) !== -1
);
return (
<div>
<PhoneForm
onCreate={this.handleCreate}
/>
<p>
<input
placeholder="검색 할 이름을 입력하세요.."
onChange={this.handleChange}
value={keyword}
/>
</p>
<hr />>
<PhoneInfoList
data={filteredList}
onRemove={this.handleRemove}
onUpdate={this.handleUpdate}
/>
</div>
);
}
}
export default App;
필터링이 잘되었는지 확인해보자!, 참고로 지금 상황에서는 키워드 값에 따라 PhoneInfoList가 전달받는 data가 다르므로, 키워드 값이 바뀌면
shouldComponentUpdate 도 true를 반환하게 된다.
04. 계속해서 최적화
render() {
console.log('render PhoneInfo ' + this.props.info.id);그 다음에, 새 데이터를 등록하고나서 개발자 콘솔을 확인하면
콘솔을 보면 처음 렌더링이 되었을 때 0과 1이 렌더링이 되었다. 그 다음에, 새 ㅔ이터가 나타났을 때 사실상 맨마지막 데이터만 새로 렌더링해주면 되는데,
그 위에있는 컴포넌트도 렌더링 되엇다. 이것도 아까 다뤘던것과 마찬가지로 실제로 바뀌지 않는 컴포넌트들은 DOM 변화가 일어나지는 않겠지만,
Virtual DOM에 그리는 자원도 아껴주기 위해서 우리는 shouldComponentUpdate를 통하여 최적화 해줄 수 있다.
// file: src/components/PhoneInfo.js
shouldComponentUpdate(nextProps, nextState) {
// 수정 상태가 아니고, info 값이 같다면 리렌더링 안함
if (!this.state.editing
&& !nextState.editing
&& nextProps.info === this.props.info) {
return false;
}
// 나머지 경우엔 리렌더링함
return true;
}
...
낭비 렌더링이 사라졌다!
이 글은 Velopert님의 블로그에서 React 포스팅을보며 실습하며 공부한 자료입니다.
'Prev-content' 카테고리의 다른 글
| [ Riot Games API 활용 전적 검색 App 02] - Riot Games API Key 신청 (5) | 2018.07.05 |
|---|---|
| [ Riot Games API 활용 전적 검색 App 01] - Project Info (0) | 2018.07.05 |
| [ React-Tutorial-08 ] 배열다루기2 제거와 수정 (0) | 2018.06.05 |
| [ React-Tutorial-07 ] 배열다루기1 생성과 랜더링 (0) | 2018.05.30 |
| [ React-Tutorial-06 ] input 상태관리 (0) | 2018.05.30 |