상태 관리 라이브러리
규모가 큰 애플리케이션에서, 컴포넌트 내에서 상태를 관리하는 것은 복잡하고 유지보수가 어렵다.
이 문제를 해결하기 위해 Angular, Ember, backgone과 같은 프레임워크와 라이브러리들이 나왔다. 그러나 특히 Angular1의 경우 양방향 데이터 흐름(bidirectional data flow)을 사용한다.
React 컴포넌트와 분리해서 상태를 관리하는 Flux architecture가 도입되었는데, unidirectional data flow(단방향 데이터 흐름)으로 데이터가 한 방향으로만 흐른다.
Redux는 Flux architecture를 이은 상태 관리 라이브러리이다.
Redux는 자바스크립트 앱에서 예측 가능한 상태 관리를 해주는 컨테이너로서, 리액트나 앵귤러같은 라이브러리와 상호작용하거나, 독자적으로 사용될 수 있다.
Flux architecture
Flux vs. Redux
View -> Action -> Reducer(s) -> Store -> View
{
type: 'Add_To_Basket',
id: 0
}
dispatching: Action을 실행하는 것. store에 있는 state를 변경하기 위해 action을 실행할 수 있다.
Action 실행, 즉 dispatching은 view의 버튼 클릭 등에 의해 일어날 수 있다.
dispatching이 일어나면 action의 정보들이 reducer를 통하게 된다.
// wrong
function reducer(state, action) {
state.push(action.id)
return state;
}
// do it like this!
function reducer(state, action) {
return state.concat(action.id)
}
function reducer(state, action) {
switch(action.type) {
case 'ADD_TO_BASKET':
return {
// do something
}
case 'REMOVE_FROM_BASKET':
return {
// do something
}
default:
return state;
}
}
상태 업데이트에 자주 쓰이는 메소드
import { createStore } from 'redux';
const store = createStore(reducer);
const store = createStore(reducer, [])
react와 함께 사용할 경우 reducer를 통해 상태 초기화를 할 수 있다.
dispatch
// dispatch an action
store.dispatch({
type: 'REMOVE_FROM_BASKET',
id: 0
})
// store의 global state 불러오기
store.getState();
// store의 활성화(subscribe)/비활성화(unsubscribe)
// 상태가 바뀔 때 마다 활성화 => 새로운 상태를 불러오게 된다.
const unsubscribe = store.subscribe(() => {
console.log(store.getState());
});
unsubscribe()
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementByI('root')
)
//..
import Stories from './Stories';
const App = () => {
<div className="app">
<Stories />
</div>
}
export default App;
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
// redux store에서 react 컴포넌트로 state의 일부를 props로 매핑
}
const mapDispatchToProps = (dispatch) => {
// props를 통해 react 컴포넌트로 redux action을 함수 형태로 전달
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Stories)
mapStateToProps
mapDispatchToProps
View -> (mapDispatchToProps) -> Action -> Reducer(s) -> Store -> (mapStateToProps) -> View
useSelector()
const result : any = useSelector(selector : Function, equalityFn?: Function)
const counter = useSelector(state => state.counter);
import { shallowEqual, useSelector } from 'react-redux';
const selectedData = useSelector(selectorReturningObject, shallowEqual)
useDispatch()
import { useDispatch } from 'react-redux';
export const Counter = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'NUMBER_UP' })}
Increase number
</button>
</div>
)
}