참고 : https://deminoth.github.io/redux/
출처 : https://velopert.com/node-js-tutorials
1) Single Source of Truth
> 어플리케이션의 state를 위해 단 한개의 store를 사용함
(Flux와 주요 차이. Flux에서는 여러개의 store를 사용)
2) State is Read-only
> 어플리케이션에서 store의 state를 직접 변경할 수 없음.
> state를 변경하기 위해선 무조건 action이 dispatch 되어야 함.
3) Changes are made with pure Fuctions
> action객체를 처리하는 함수는 reducer
> reducer는 정보를 받아서 상태를 어떻게 업데이트 할 지 정의
> reducer는 '순수 함수'로 작성되어야 함
즉, 네트워크 및 데이터베이스 접근 X, 인수 변경 X
같은 인수로 실행된 함수는 언제나 같은 결과를 반환
순수하지 않은 API는 사용 불가 (Date.now(), Math.random() 등)
cf. REDUX로의 카툰 안내서 : https://bestalign.github.io/2015/10/26/cartoon-intro-to-redux/
- store : React.js 프로젝트에서 사용하는 모든 동적 데이터들을 담아두는 곳
createStore( Reducer ); store를 생성 한다.
dispatch 함수를 여기서 꺼내서 사용할 수 있다.
dispatch(action);
액션 객체를 reducer로 보냄, store의 reducer 함수는 현재 getState() 결과 값과 호출 될 것이고 동시에 action이 주어질 것이다. 그 반환 값은 다음 state로 간주 될 것이다.
반환 값 : 보내진 action
getState(); 현재 상태 반환, store의 reducer에 의한 최근 값과 같음
subscribe(listener); 상태가 바뀔 때마다 실행 할 함수를 등록
replaceReducer(nextReducer)
hot reloading과 코드 분할할 때 사용
- action(액션 객체) :
어떤 변화가 일어나야 할 지 나타내는 객체
작업에 대한 정보를 가지고 있는 객체
1) 첫번째 필드는 type이며 필수적인 필드로서 action의 형태를 정의
2) 그 다음은 개발자 마음대로 추가, 필요없으면 생략 가능 (reducer에서 사용할 파라미터 역할)
cf, redux-actions 라는 좋은 모듈이 있다. (createAction() : 액션 생성, handleAction() : reducer )
handleAction은 기존 reduer에서 switch case 분기 후 다음 적용 될 state를 반환하는 것과 달리 action을 키값으로 함수를 설정한 객체를 갖고 있도록 한다.
- reducer
action 객체를 받았을 때, 데이터를 어떻게 바꾸고 처리할 지 정의하는 객체
변화를 일으키는 함수.
순수해야함 (비동기 작업x, 인수 변경x, 동일한 인수 = 동일한 결과)
-> 이전 상태값을 가지고 비교하여서 갱신하기 때문에 값을 받은 값을 바로 고치는 것은 안됨
-> 새로운 객체에 상태를 담아서 보내게 됨 ( store와 연결 시켜야 하기 때문 )
( this.state를 바로 바꾸지 못하고 setState()를 사용하는 것을 생각하면 된다. )
이전 상태와 액션을 받아서 다음 상태(새로운 객체)를 반환
(기존 상태를 복사하고 변화를 준 다음 반환)
우선 데이터의 초기 상태를 정의하고 arrow function(람다 함수)을 통하여 reducer를 만든다.
* state를 바로 변경시키지 않고 Object.assign()을 통하여 state를 복사한 후
복사된 객체를 수정하여 반환한다.
* 첫번째 인자는 꼭 빈 객체이어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const initialState = { value: 0 }; const counterReducer = (state = initialState, action) => { switch(action.type) { case INCREMENT: return Object.assign({}, state, { value: state.value + action.addBy }); default: return state; } } | cs |
- combine reducers (액션이 무엇인지에 따라 동작)
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 32 33 34 35 36 37 | const counterInitialState = { value: 0, diff: 1 }; const counter = (state = counterInitialState, action) => { switch(action.type) { case INCREMENT: return Object.assign({}, state, { value: state.value + state.diff }); case DECREMENT: return Object.assign({}, state, { value: state.value - state.diff }); case SET_DIFF: return Object.assign({}, state, { diff: action.diff }); default: return state; } }; const default_value = {value: 'this_is_extra_reducer'}const extra = (state = default_value, action) => { switch(action.type) { default: return state; } } const counterReducer = combineReducers({ counter, extra }); // 다음과 같이 store의 state 구조가 생성됨 // { // counter: { value: 0, diff: 1 } // extra: { value: 'this_is_extra_reducer' } // }export default counterReducer; | cs |
1 2 3 4 5 6 | const counterReducer = ( state = { }, action ) => { return { counter: counter(state.counter, action), extra: extra(state.extra, action) } } | cs |
* 각 reducer에 다른 key값을 줄 수도 있다.
1 2 3 4 | const counterApp = combineReducers({ a: counter, b: extra }); | cs |
- connect()
> react-redux 내장 API
> connect( [mapStateToProps], [mapDispatchToProps], [mergeProps], [option] )
* mapDispatchToProps를 사용할 때 두번째 자리이므로 따로 사용할 경우 첫번째 자리에 null이나 undefined를 넣어서 맞춰줌
> React Component를 ReduxStore에 연결 해줌
> 리턴값은 특정 컴포넌트 클래스의 props를 store의 데이터에 연결 시켜주는 또 다른 함수를 리턴.
리턴된 함수에 컴포넌트를 인수로 넣어 실행하면, 기존 컴포넌트를 수정하는 게 아니라 새로운 컴포넌트를 return
( Component = connect( ... )(Component); 반환된 함수의 인자값으로 Component를 넣으므로 이런 형태가 됨)
> Parameters (간결하게 보이기 위해 람다 함수로 넣어줘도 된다.)
* mapStateToProps(state, [ownProps])
혹은 (state) => ({ Props })
: (Function) store의 state를 컴포넌트의 props에 매핑 시켜준다.
own Props 인수가 명시될 경우, 이를 통해 함수 내부에서 컴포넌트의 props 값에 접근 할 수 있다. ( reducer구조 혹은 combineReducers에서 만들었던 구조대로 state 구조가 됨
이를 이용하여 원하는 state 내부에 접근 )
* mapDispatchToProps(dispatch, [ownProps])
혹은 (dispatch) => ({ Props })
: (Function of Object) 컴포넌트의 특정 함수형 props를 실행 했을 때, 개발자가 지정한 action을 dispatch 하도록 설정한다.
ownProps의 용도는 위 인수와 동일하다.
> bindActionCreators(actions, dispatch);
액션과 dispatch함수를 연결하도록 한다. 일일이 액션마다 bind해주지 않고 한꺼번에 가능
넘길 때 이름을 매겨서 Container에서 prop으로 꺼낼때 이 이름으로 함수를 묶어서 관리 가능
보통은 mapDispatchToProps 함수에 넣어서 사용하지만 따로 꺼내서 store.dispatch에 바로 붙여서 사용 가능함