2019. 8. 18. 01:39ㆍ[정리] 기능별 개념 정리/React
Redux
- Facebook 에서 주도하여 개발
- Flux 아키텍쳐를 구현한 라이브러리
- Flux 와 완전히 같다고 할 수는 없다.
MVC 의 문제점과 Flux 아키텍처가 필요한 이유 : http://bestalign.github.io/2015/10/06/cartoon-guide-to-flux/
Redux 가 Flux 를 완전히 따라 할 수 없는 이유 : http://bestalign.github.io/2015/10/26/cartoon-intro-to-redux/
Redux 개념
- action : 발생하는 상황. 어떤 작업인지 정보를 가진 객체
- action creator : 어떤 값을 업데이트 하기위해 어떤 action 을 발생시킬지 결정한다.
- dispatcher : 액션을 읽고 어떤 일을 할지 처리한다. 동기 처리된다. 작업이 중첩되지 않도록 도와준다.
- store : 애플리케이션의 현재 state 를 저장하는 공간이다. (reducer 들이 관리하는 state 들은 모두 여기에 모인다.)
- store.getState() : 현재 상태를 받는다.
- store.dispatch(action) : action 이 발생했음을 알린다.
- store.subscribe(listener) : store 안의 데이터가 update 됬으면 listener (callback 메소드)를 실행한다.
- reduce : Action 객체를 보고 어떤 일이 발생했는지에 따라 어떻게 처리할지 결정하는 함수다.
- reduce 함수는 순수 함수여야한다.
순수 함수
- 비동기 작업을 처리해선 안된다. (ex. API 콜, DB와의 IO, 등...)
- 넘어오는 Argument 값을 변경해선 안된다.
- 같은 Arument 에 대해 같은 결과가 실행되어 나와야한다.
Redux 의 3가지 주요 원칙
- 애플리케이션의 state 를 위해 단 하나의 Store 를 사용한다.
- State 는 반드시 dispatch(action) 으로만 변경되어야한다. 직접 변경해선 안된다.
- Reducer 는 순수 함수이여야 한다.
데이터 플로우
- 사용자가 버튼을 누른다.
- 버튼을 누르는 행위는 this.props.handlerA를 실행시킨다.
- this.props.handlerA는 dispatch({type:"A"})에 맵핑되어있다.
- dispatch에 의해 reducer가 실행된다.
- reducer는 action.type == "A" 일 경우 어떤 로직을 취하라고 되어있다.
- reducer는 state 를 변경시키고 newState 를 반환한다.
- 해당 state는 어떤 컴포넌트의 props 와 연결되있다. React는 props의 변경을 파악하고 렌더링을 다시한다.
- 해당 state는 store 에 의해 감시되고 있다.
- 해당 state에 변경이 이루어 졌으므로 수행해야하는 subscribe listener (callback) 를 실행한다.
강의에서는 배경화면을 변경하는 것도 다루고 있지만 편의상 배경화면 변경은 빼고 진행한다.
강의 예시 원본 프로젝트 : https://bitbucket.org/velopert/redux-example/src/master/
project
---- /node_module
---- /src
-------- /actions
-------- /components
-------- /reducers
-------- index.js
index.html
package.json
webpack.config.js
myProject/src/actions/ActionTypes.js
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
myProject/src/actions/index.js
import * as types from './ActionTypes';
export function increment() {
return { type: types.INCREMENT };
}
export function decrement() {
return { type: types.DECREMENT };
}
import * as types : 데이터를 가져오는 데 타입으로 가져와라
Action 의 이름은 반드시 대문자와 언더스코어를 이용해서 이름을 정한다.
Action 은 객체다.
Action 은 Type 필드를 반드시 갖는다.
Action 안에 원하는 데이터 필드를 넣을 수 있다.
Action 안의 원하는 데이터 필드에 dispatch 할 때 데이터를 넣을 수 있다.
Action 안의 원하는 데이터 필드에 dispatch 할 때 넣은 데이터를 reducer 가 읽을 수 있다.
myProject/src/reducers/index.js
import { combineReducers } from 'redux';
import * as types from '../actions/ActionTypes';
const initialState = {
number: 0,
};
export default function counterReducer(oldState = initialState, action) {
let newState = { ...oldState };
switch(action.type) {
case types.INCREMENT:
newState.number = oldState.number + 1;
case types.DECREMENT:
newState.number = oldState.number - 1;
}
return newState;
}
const reducers = combineReducers({
counterReducer
});
export default reducers;
reducer 는 (oldState, action) 을 인자로 가진다.
reducer 는 newState 를 반환한다.
reducer 를 여러 파일로 쪼개서 관리할 수 있다.
이 때 쪼개진 reducer 들을 합치기 위해 combineReducers 를 이용한다.
reducer 안의 데이터 변경은 반드시 새로운 것 만들고 이를 복사해서 반환하는 형식이여야한다.
myProject/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Application from './components/Application';
import { createStore } from 'redux';
import reducers from './reducers';
import { Provider } from 'react-redux';
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
createStore(reducers) 를 통해 reducer 들을 수집함과 동시에 reducer 들이 바라보는 state 를 관리한다.
Provider 를 통해 store 를 지정한다.
myProject/src/components/Application.js
import React, { Component } from 'react';
import Counter from './Counter';
class Application extends Component {
render() {
return(
<Counter/>
);
}
}
export default Application;
myProject/src/components/Counter.js
import React, { Component } from 'react';
import CounterValue from './CounterValue';
import CounterButtons from './CounterButtons';
import { connect } from 'react-redux';
import * as actions from '../actions';
class Counter extends Component {
render() {
return(
<div>
<CounterValue number={this.props.number}/>
<CounterButtons
onPlus={this.props.handleIncrement}
onSubtract={this.props.handleDecrement}/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
number: state.counterReducer.number
};
};
const mapDispatchToProps = (dispatch) => {
return {
handleIncrement: () => { dispatch(actions.increment())},
handleDecrement: () => { dispatch(actions.decrement())},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
myProject/src/components/CounterValue.js
import React, { Component, PropTypes } from 'react';
const propTypes = {
number: PropTypes.number
};
const defaultProps = {
number: -1
};
class CounterValue extends Component {
render() {
return(
<div>
<h1>{this.props.number}</h1>
</div>
);
}
}
CounterValue.propTypes = propTypes;
CounterValue.defaultProps = defaultProps;
export default CounterValue;
myProject/src/components/CounterButtons.js
import React, { Component, PropTypes } from 'react';
const propTypes = {
onPlus: PropTypes.func,
onSubtract: PropTypes.func,
};
const defaultProps = {
onPlus: () => console.warn('onPlus is not defined');
onSubtract: () => console.warn('onSubtract is not defined');
};
class CounterButtons extends Component {
render() {
return(
<div>
<button onClick={this.props.onPlus}>+</button>
<button onClick={this.props.onSubtract}>-</button>
</div>
);
}
}
CounterButtons.propTypes = propTypes;
CounterButtons.defaultProps = defaultProps;
export default CounterButtons;
mapStateToProps : Reducer 안에서 관리하는 값을 컴포넌트의 props 로 맵핑한다.
mapDispatchToProps : 컴포넌트의 props 에서 어떤 커맨드가 실행되면 어떤 것을 store.dispatch(action) 할지 맵핑한다.
connect(mapStateToProps, mapDispatchToProps)(Counter);
Smart 컴포넌트
- Redux 와 연관 O
- state 를 subscribce 하고 action 을 보낸다.
- DOM, CSS 를 관리하지 않는다.
- 여기서는 Counter 가 똑똑한 컴포넌트다.
Dumb 컴포넌트
- Redux 와 연관 X
- props 에서 데이터를 읽는다
- props 에서 callback 을 부른다.
- DOM, CSS 를 관리한다.
- 여기서는 CounterValue, CounterButtons 이 멍청한 컴포넌트다.
주요 개발 흐름
- Action 정의
- Reducer 생성, 로직 처리
- Reducer state 와 특정 컴포넌트의 props 아래 값과 연결 (mapStateToProps)
- 특정 컴포넌트 props 아래 식별자와 dispatcher 연결 (mapDispatchToProps)
- Reducer state 를 store 에 등록 (createStore(reducers))
- store 의 범위 지정 (<Provider store={store}> <Application> </Provider>)
사견 : Action 은 사실상 타입으로 정의만 해두는 것이고 dispatch 하면 consume 하는 reducer 가 주요 작업인 듯하다. 그래서 의미적으로 Reduce + flux 가 된 것이 아닌가 싶다.
'[정리] 기능별 개념 정리 > React' 카테고리의 다른 글
React 기본 강좌 정리 - 1~2 (0) | 2020.02.11 |
---|---|
React 강의 정리 (6) (0) | 2019.08.23 |
React 강의 정리 (3,4) (0) | 2019.08.18 |
React 강의 정리 (2) (0) | 2019.08.18 |
React 강좌 정리 (1) (0) | 2019.08.17 |