Back Ground

React - Redux 기초 작업 본문

Javascript/React.js

React - Redux 기초 작업

Back 2019. 2. 3. 20:54



긴 설명 보다 내가 보고 쓰기 편하게 단순하게 

이해 할 수 있게 쓰겠다. 



먼저 보일러 플레이트 중 

create-react-app을 통해서 프로젝트를 생성 해 줄 것이다.


redux-setting 이라는 디렉토리로 만들었다

create-react-app redux-setting 


이제 redux를 사용하기 위해 몇가지 모듈을 받아준다.

npm install redux react-redux




이제 불필요한 부분을 지워준다.

(기존 index 코드 부분이나 App 부분) 








시작하기 전 리덕스의 구조를 생각해 둔다.





src 안에 디렉터로리를 나눠서 구조를 갖춰준다.

  • actions
  • components
  • containers
  • reducers




이제

리덕스 구조 그림을 참고하고 순서 대로 만들어 준다.

그러면 헤깔리지 않고 만들 수 있게 된다.





가장 먼저

ser > store.js를 만들어준다.











store


src/store.js

1
2
3
4
5
import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
 
const logger = createLogger();
export default createStore(/*reducer가 들어가 추가 될 곳,*/applyMiddleware(logger));
cs

tip : 여기서 redux-logger 는 redux의 상태를 로그로 보여주는 모듈이다. 로그 관리하고 보기 편하다 ( 선택 )

npm install redux-logger



store를 만들었으니 사용하기 위해 가장 먼저 실행되는 index.js에다가 store를 참조한다.



src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
 
import store from './store';
import { Provider } from 'react-redux';
 
 
ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
document.getElementById('root'));
serviceWorker.unregister();
 
cs

Provider는 리덕스를 컴퍼넌트로부터 연결하기 쉽게 만들어 준다.

store를 참조해서 만들어 준다.



다음은 

action나 reducer를 만들어 주면 된다.

action쪽을 먼저 만들어 두는 편이 편하기 때문에 aciton을 먼저 작업한다.












action


actions 디렉토리에 

  • actionTyeps.js
  • index.js

를 추가 해준다.




actionTyeps.js

1
2
export const _CREATE = 'CREATE';
export const _UPDATE = 'UPDATE';
cs



actions/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as types from './actionTyeps';
 
export const _create = () =>{
    return {
        type: types._CREATE
    }
}
 
export const _update = (index) =>{/*인자는 리듀서에서  처리 필요 시 추가 */
    return {
        type: types.UPDATE,
        index 
    }
}
cs






다음은 Reducer를 만들어준다.











Reducer





reducers/reducer_setting.js 

1
2
3
4
export default [
    {name:'test1' ,index:0text:'setting_' ,boolean:false},
    {name:'test2' ,index:0text:'test_'    ,boolean:false},
]
cs

state 초기값을 지정해 주면 된다.


디렉토리명이 reducer 라도 파일 네임앞에 reducer_를 붙여주는것이 좋다.  (비슷한 파일명을 다루기 때문에 혼동을 방지하기 위함)





reducers/index.js

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
38
39
import Reducer_setting from './reducer_setting';//state 초기값을 가져온다.
import * as tyeps from '../actions/actionTyeps'//액션 코드로 가져온다.
import { combineReducers } from 'redux'//다중 리듀서를 사용할때 리듀서를 넘겨주기 위한 용도
 
 
const root_reducer = {
    setting : Reducer_setting,
 
 
function redux_settingData (state = root_reducer, action){ 
    //state는 reducer가 갖고 있는 state고 만약 없을시 초기값을 가져온다.
    //action은 dispatch함수로 connect를 통해서 들어 오게 된다.
 
    const {setting} = state;
 
    switch (action.type){//리듀서를 통해서 들어온 action 
        case tyeps._CREATE: //acionTyeps에서 구분
           
            // setState처럼 사용 할 부분
            return { setting :  //setting : 한 이유는 초기 setting을 했기에 변경 후에도 setting key를 유지하기 위함 
                {...setting}.concat(action.data)
            } 
        
        case tyeps._UPDATE: //acionTyeps에서 구분
            return { setting : setting.map( (key, i) => {
                    if(i === action.index){ //action index.js에서 지정한 index
                        return {...key, boolean:true}
                    }else{
                        return {...key};
                    }
                })
            }    
        defaultreturn state; //action을 이용하지 않을때 기본 this.props.setting을 사용할 때 사용
    }
 
 
export default combineReducers({redux_settingData}); //다중으로 사용 되기 때문에 JSON타입 특성상 redux_settingData키로 값이 들어가기 때문에 this.propsredux_settingData.setting 가 된다.
cs




이 부분 reducer의 핵심 부분이다. 리듀서를 호출할 때 이 함수를 통해서 변경된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function redux_settingData (state = root_reducer, action){  
    
    const {setting} = state;
 
    switch (action.type){
        case tyeps._CREATE: 
            return { setting : 
                {...setting}.concat(action.data)
            } 
        
        case tyeps._UPDATE: 
            return { setting : setting.map( (key, i) => {
                    if(i === action.index){
                        return {...key, boolean:true}
                    }else{
                        return {...key};
                    }
                })
            }    
        defaultreturn state;
    }
 
}
cs



다중으로 사용 되기 때문에 JSON타입 특성상 redux_settingData키로 값이 들어가기 때문에 this.propsredux_settingData.setting가 된다.

1
export default combineReducers({redux_settingData}); 
cs





src/store.js

1
2
3
4
5
6
import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import reducers from './reducers';
 
const logger = createLogger();
export default createStore(reducers,applyMiddleware(logger));
cs

그 후 스토어에 reducer를 연결해준다.




다음은 dispatch를 리듀서로 보낸다.



dispatch


containersApp.js를 만든다.





App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { PureComponent } from 'react';
 
import * as actions from '../actions';
import { connect } from 'react-redux';
 
class App extends PureComponent {
    render() {
        return (
            <div className="App">
                
            </div>
        );
    }
}
 
//액션 생성 함수 준비
const mapToDispatch = (dispatch) =>({
    onCreate: (data)  => dispatch(actions._create(data)),
    onUpdate: (index) => dispatch(actions._update(index))
});
 
// 리덕스에 연결시키고 내보냅니다.
export default connect(null, mapToDispatch)(App);
cs

꼭 App.js가 아니더라도 필요한 부분의 컴포넌트에 넣으면 된다.



1
2
3
4
5
6
7
8
//액션 생성 함수 준비
const mapToDispatch = (dispatch) =>({
    onCreate: (data)  => dispatch(actions._create(data)),
    onUpdate: (index) => dispatch(actions._update(index))
});
 
// 리덕스에 연결시키고 내보냅니다.
export default connect(null, mapToDispatch)(App);
cs

리덕스에 연결을 시켜 props로 받아서 사용이 가능하다.







App.js에서 리듀서를 통해서 받으면 props로 받을 수 있고 

하위컴포넌트로 props로 보내면 된다.

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
import React, { PureComponent } from 'react';
 
import * as actions from '../actions';
import { connect } from 'react-redux';
 
import Setting from '../components/setting';//컴포넌트
 
class App extends PureComponent {
    render(){
        const {onCreate,onUpdate} = this.prop;
        return (
            <div className="App">
                <Setting 
                    onCreate={onCreate}
                    onUpdate={onUpdate}
                />
            </div>
        );
    }
}
 
//액션 생성 함수 준비
const mapToDispatch = (dispatch) =>({
    onCreate: (data)  => dispatch(actions._create(data)),
    onUpdate: (index) => dispatch(actions._update(index))
});
 
// 리덕스에 연결시키고 내보냅니다.
export default connect(null, mapToDispatch)(App);
cs




디스페치로 리듀서를 통해서 props 받아서 이런식으로 처리하면 된다.
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
import React, { PureComponent } from 'react';
 
import * as actions from '../actions';
import { connect } from 'react-redux';
 
class App extends PureComponent {
    
    render(){
        console.log(this.props);
        return (
            <div className="App">
               {this.props.redux_settingData.setting.map( (key) => { return key.name })}
                <input type='button' onClick={()=>this.props._update(1} }/>
            </div>
        );
    }
}
 
//액션 생성 함수 준비
const mapToDispatch = (dispatch) =>({
    _create: (data)  => dispatch(actions._create(data)),
    _update: (index) => dispatch(actions._update(index))
});
 
function mapStateToProps(state){
    //여기에서 state인자란 리듀서에서의 state이다.
    console.log("mapStateToProps : ",state);
    return state;
  }
 
// 리덕스에 연결시키고 내보냅니다.
export default connect(mapStateToProps, mapToDispatch)(App);
cs











최종



src/store.js

1
2
3
4
5
6
import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import reducers from './reducers';
 
const logger = createLogger();
export default createStore(reducers,applyMiddleware(logger));
cs



src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import * as serviceWorker from './serviceWorker';
 
import store from './store';
import { Provider } from 'react-redux';
 
ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
document.getElementById('root'));
serviceWorker.unregister();
cs




actions/actionTyeps.js

1
2
export const _CREATE = 'CREATE';
export const _UPDATE = 'UPDATE';
cs



actions/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import * as types from './actionTyeps';
 
export const _create = (data) =>{
    return {
        type: types._CREATE,
        data
    }
}
 
export const _update = (index) =>{/* 인자는 리듀서에서 처리 시 추가 */
    return {
        type: types._UPDATE,
        index 
    }
}
cs





reducers/reducer_setting.js

1
2
3
4
5
export default [
    {name:'test1' ,index:0text:'setting_' ,boolean:false},
    {name:'test2' ,index:1text:'test_'    ,boolean:false},
]
 
cs



reducers/index.js

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
38
39
import Reducer_setting from './reducer_setting';//state 초기값을 가져온다.
import * as tyeps from '../actions/actionTyeps'//액션 코드로 가져온다.
import { combineReducers } from 'redux'//다중 리듀서를 사용할때 리듀서를 넘겨주기 위한 용도
 
 
const root_reducer = {
    setting : Reducer_setting,
 
 
function redux_settingData (state = root_reducer, action){ 
    //state는 reducer가 갖고 있는 state고 만약 없을시 초기값을 가져온다.
    //action은 dispatch함수로 connect를 통해서 들어 오게 된다.
 
    const {setting} = state;
    console.log("redux : ", state);
    switch (action.type){//리듀서를 통해서 들어온 action 
        case tyeps._CREATE: //acionTyeps에서 구분
           
            // setState처럼 사용 할 부분
            return { setting :  //setting : 한 이유는 초기 setting을 했기에 변경 후에도 setting key를 유지하기 위함 
                {...setting}.concat(action.data)
            } 
        
        case tyeps._UPDATE: //acionTyeps에서 구분
            return { setting : setting.map( (key, i) => {
                    if(i === action.index){ //action index.js에서 지정한 index
                        return {...key, boolean:true}
                    }else{
                        return {...key};
                    }
                })
            }    
        defaultreturn state; //action을 이용하지 않을때 기본 this.props.setting을 사용할 때 사용
    }
 
 
export default combineReducers({redux_settingData}); //다중으로 사용 되기 때문에 JSON타입 특성상 redux_settingData키로 값이 들어가기 때문에 this.propsredux_settingData.setting 가 된다.
cs






containers/App.js

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
import React, { PureComponent } from 'react';
 
import * as actions from '../actions';
import { connect } from 'react-redux';
 
class App extends PureComponent {
    
    render(){
        console.log(this.props);
        return (
            <div className="App">
               {this.props.redux_settingData.setting.map( (key) => { return key.name })}
                <input type='button' onClick={()=>this.props._update(1) } }/>
            </div>
        );
    }
}
 
//액션 생성 함수 준비
const mapToDispatch = (dispatch) =>({
    _create: (data)  => dispatch(actions._create(data)),
    _update: (index) => dispatch(actions._update(index))
});
 
function mapStateToProps(state){
    //여기에서 state인자란 리듀서에서의 state이다.
    console.log("mapStateToProps : ",state);
    return state;
  }
 
// 리덕스에 연결시키고 내보냅니다.
export default connect(mapStateToProps, mapToDispatch)(App);
cs






Comments