React 불변성을 지키는 이유와 업데이트 최적화
데이터 필터링 구현하기
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 } // 새 객체를 만들어서 기존의 값과 전달받은 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의 값을 state의 keyword라는 값에 담음
PhoneInfoList 컴포넌트의 render함수
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>
);
}
- App이 리렌더링됨에 따라 PhoneInfoList도 Virtual DOM에 리렌더링됨
- Virtual DOM에 낭비되는 자원을 아끼기 위해 shouldComponentUpdate LifeCycle API 가 사용됨
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;
- 단순히 다음 받아올 data 가 현재 data 랑 다른 배열일 때 true 로 설정
- 이제 변화가 필요하지 않을 때는 render 함수가 호출되지 않게 됨
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 } // 새 객체를 만들어서 기존의 값과 전달받은 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;
- App 컴포넌트에서 keyword 값에 따라 information 배열을 필터링 해주는 로직을 작성, 필터링된 결과를 PhoneInfoList에 전달
- 키워드 값에 따라 PhoneInfoList 가 전달받는 data 가 다르므로, 키워드 값이 바뀌면 shouldComponentUpdate 도 true 를 반환
shouldComponentUpdate 를 통하여 최적화
shouldComponentUpdate(nextProps, nextState) {
// 수정 상태가 아니고, info 값이 같다면 리렌더링 안함
if (!this.state.editing
&& !nextState.editing
&& nextProps.info === this.props.info) {
return false;
}
// 나머지 경우엔 리렌더링함
return true;
}
...
Comments