해당 블로그를 보고 공부하면서 정리한 글입니다.
객체의 불변성
let a = 7;
let b = 7;
let object1 = { a: 1, b: 2 };
let object2 = { a: 1, b: 2 };
object1과 object3 === 비교를 하면 결과는 무엇일까?
object1 === object2
// false
아래와 같이 하면 또 다른 결과를 얻을 수 있다.
let object3 = object1
object1 === object3
// true
하지만 이렇게 할당을 하면 한가지 문제가 있다.
같은 주소를 바라보기 때문에 하나의 객체를 수정하면 같이 수정이 된다.
object3.c = 3;
object1 === object3
// true
object1
// Object { a: 1, b: 2, c: 3 }
같은 주소를 바라보고 있기 때문에 수정을 하여도 항상 같은 값을 가진다.
하지만!
리액트 컴포넌트에서는 state 혹은 상위 컴포넌트에서 전달받은 props의 값이 변할 때 리랜더링을 해야 하는데, 만약에 객체를 직접 수정하면 내부의 값이 수정되더라도 래퍼런스가 가리키는 곳은 같기 때문에 똑같은 값으로 인식한다.
이러한 이슈 때문에, ... 을 사용해 기존의 값을 이용하여 새로운 객체를 만들어야 했다.
하지만 이 방법 때문에 간단한 작업도 상당히 복잡해 진다.
let object1 = {
a: 1,
b: 2,
c: 3,
d: {
e: 4,
f: {
g: 5,
h: 6
}
}
};
// h값을 10으로 업데이트함
let object2 = {
...object,
d: {
...object.d,
f: {
...object.d.f,
h: 10
}
}
}
이러한 작업을 간소화하기 위해 만들어진 것이
페이스북팀이 만든 라이브러리 immutable.js 이다.
let object1 = Map({
a: 1,
b: 2,
c: 3,
d: Map({
e: 4,
f: Map({
g: 5,
h: 6
})
})
});
let object2 = object1.setIn(['d', 'f', 'h'], 10);
object1 === object2;
// false
직관적으로 객체를 수정할 수 있게 되었다.
Map
var Map = Immutable.Map;
var data = Map({
a: 1,
b: 2,
c: Map({
d: 3,
e: 4,
f: 5
})
})
Map을 자바스크립트 객체로 변환하려면
data.toJS(); // { a:1, b:2, c: { d: 3, e: 4 } }
다음과 같이 해주면 된다.
Map의 특정 값을 가져오는 방법은 2가지가 있다.
data.get('a'); // 1
data.getIn(['c', 'd']) // 3
값을 수정하려면 아래와 같이 하면 된다.
다만, data가 변하는게 아니고 새로운 Map이 생성된다는 것을 기억하자.
var newData = data.set('a', 4);
var newData = data.setIn(['c', 'd'], 10);
여러개의 값을 설정하려면 아래와 같이 하면 된다.
var newData = data.mergeIn(['c'], { d: 10, e: 10 });
var newData = data.setIn(['c', 'd'], 10);
.setIn(['c', 'e'], 10);
var newData = data.merge({ a: 10, b: 10 })
하지만 merge 하는것 보다 set을 여러번하는게 성능상 더 좋다.
List
var List = Immutable.List;
var list = List([0,1,2,3,4]);
리스트에 아이템 추가하기
var newList = list.push(Map({value: 3}))
// 맨 앞에 추가하고 싶다면
var newList = list.unshift(Map({value: 0}))
리스트에 아이템 삭제하기
var newList = list.delete(1);
// 가장 마지막 제거
var newList = list.pop();
리덕스에서 사용하기
이제 실제 코드에 적용을 해보자.
import { Map, List } from immutable;
const initialState = Map({
counters: List([
Map({
color: 'black',
number: 0
})
])
})
// 리듀서 함수를 정의합니다.
function counter(state = initialState, action) {
const counters = state.get('counters');
switch(action.type) {
// 카운터를 새로 추가합니다
case types.CREATE:
return state.set('counters', counters.push(Map({
color: action.color,
number: 0
})))
// slice 를 이용하여 맨 마지막 카운터를 제외시킵니다
case types.REMOVE:
return state.set('counters', counters.pop());
// action.index 번째 카운터의 number 에 1 을 더합니다.
case types.INCREMENT:
return state.set('counters', counters.update(
action.index,
(counter) => counter.set('number', counter.get('number') + 1))
);
// action.index 번째 카운터의 number 에 1 을 뺍니다
case types.DECREMENT:
return state.set('counters', counters.update(
action.index,
(counter) => counter.set('number', counter.get('number') - 1))
);
// action.index 번째 카운터의 색상을 변경합니다
case types.SET_COLOR:
return state.set('counters', counters.update(
action.index,
(counter) => counter.set('color', action.color))
);
default:
return state;
}
};
컴포넌트 수정하기
state.counters 대신 아래와 같이 수정을 하면 된다.
// store 안의 state 값을 props 로 연결해줍니다.
const mapStateToProps = (state) => ({
counters: state.get('counters')
});
기존 배열 매핑 코드도 아래와 같이 수정을 하면 된다.
import React from 'react';
import Counter from './Counter';
import PropTypes from 'prop-types';
import { List } from 'immutable';
import './CounterList.css';
const CounterList = ({counters, onIncrement, onDecrement, onSetColor}) => {
const counterList = counters.map(
(counter, i) => (
<Counter
key={i}
index={i}
{...counter.toJS()}
onIncrement={onIncrement}
onDecrement={onDecrement}
onSetColor={onSetColor}
/>
)
);
return (
<div className="CounterList">
{counterList}
</div>
);
};
CounterList.propTypes = {
counters: PropTypes.instanceOf(List),
onIncrement: PropTypes.func,
onDecrement: PropTypes.func,
onSetColor: PropTypes.func
};
CounterList.defaultProps = {
counters: [],
onIncrement: () => console.warn('onIncrement not defined'),
onDecrement: () => console.warn('onDecrement not defined'),
onSetColor: () => console.warn('onSetColor not defined')
}
export default CounterList;
'React' 카테고리의 다른 글
[React-Native 공식문서] 1. Core Components And Native Components (0) | 2022.03.20 |
---|---|
4장. Ducks 구조와 redux-actions 사용하기 (0) | 2019.10.29 |
React vs Vue (0) | 2019.02.28 |
Redux 시작하기 기초 - (마지막) React와 함께 사용하기 (0) | 2017.11.21 |
Redux 기초 - (2) 스토어(Store), 데이터 흐름 (0) | 2017.11.21 |