3-3. 리덕스에서 사용하기
자, 리덕스에서 Immutable 을 사용하는 방법을 알아보겠습니다.
기존에 만들었던 프로젝트에서 우리가 배웠던것들을 적용해볼게요.
설치
우선, immutable 을 설치하세요.
yarn add immutable
리듀서 수정
이제 리듀서에서 immutable 을 불러오고 사용을 해보겠습니다.
src/reducers/index.js
우선 코드의 상단에서 Map 과 List 를 불러오세요.
import { Map, List } from 'immutable';
그 다음, initialState
를 Map 과 List 를 사용해서 만드세요.
const initialState = Map({
counters: List([
Map({
color: 'black',
number: 0
})
])
})
{ }
은 Map 으로, [ ]
은 List 로 하시면 됩니다.
자, 이제 리듀서를 전체적으로 처음부터 작성할것입니다. 걱정하지마세요, immutable과 함께라면 금방 합니다.
// 리듀서 함수를 정의합니다.
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;
}
};
어때요? 기존 코드보다 훨씬 간결해지고, 읽기도 쉬워졌지요?
그럼 전체코드를 확인해볼까요?
import { Map, List } from 'immutable';
import * as types from '../actions/ActionTypes';
// 초기 상태를 정의합니다.
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
})))
// pop 을 사용하여 맨 마지막 카운터를 제거합니
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;
}
};
export default counter;
컴포넌트 수정
리듀서에서 이제 immutable 을 사용하니, 컴포넌트에서도 이를 반영시켜주어야겠죠?
src/containers/CounterListContainer.js
CounterListContainer 컴포넌트의 mapStateToProps
에서, state.counters
대신 state.get('counters')
로 수정하세요.
// store 안의 state 값을 props 로 연결해줍니다.
const mapStateToProps = (state) => ({
counters: state.get('counters')
});
src/components/CounterList.js
다음, CounterList 컴포넌트에서 배열을 매핑하는 과정에서 {...counter}
를 {...counter.toJS()}
로 수정하고, propType 도 PropTypes.instanceOf(List)
로 수정하세요.
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;
이제 다 끝났습니다! 카운터가 제대로 작동하는지 확인해보세요.
여기까지의 코드는 GitHub 에서 확인 하실 수 있습니다.