본문 바로가기
web prog/React

Redux

by RedWiz 2018. 4. 4.

참고 : 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

> combineReducers는 reducer를 한 개로 합칠칠 때때 사용되는 redux 내장 메소드이다.
> reducer를 여러개로 분리하여 작성할 땐, 서로 직접적인 관계가 없어야 한다.
> state 내부에 reducer 마다 reducer 객체 이름 (혹은 키 값을 넣주어서 직접 정할 수 있음)을 넣어서 구조를 만듦 -> 이 객체 이름(혹은 키 값) 은 connect()에서 mapStateToProps 작성할 때 바로 사용

* combineReducer를 쓴 것은 다음 코드와 동일 하다.

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


 react-redux

- Provider : react-redux 패키지의 일부로 스토어의 데이터를 컴포넌트에 주입해 줌.
=> 모든 자식 컴포넌트가 스토어에 접근할 수 있다.

<Provider store = {store}>
<App>
</Provider>

- 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에 바로 붙여서 사용 가능함




'web prog > React' 카테고리의 다른 글

React  (0) 2018.04.02