2019. 8. 29. 18:24

0. 디렉터리 생성

 

1. App.js 생성

//src/containers/App.js
import React, {Component} from 'react';

class App extends Component {
    render() {
        return (
            <div>
                Counter
            </div>
        );
    }
}

export default App;

 

2. index.js 생성

//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './containers/App';

ReactDOM.render(<App />, document.getElementById('root'));

 

 

3. Counter 컴포넌트 생성

//src/components/Counter.js
//presentational component
// view를 담당, 리덕스 스토어에 직접 접근할 권한은 없으며 오직 props로만 데이터를 가져올 수 있음, 주로 함수형 컴포넌트로 작성

import React from 'react';
import PropTypes from 'prop-types';
import './Counter.css';


const Counter = ({ number, color, onIncrement, onDecrement, onSetColor }) => {
    return (
        <div
            className="counter"
            onClick={onIncrement}
            onContextMenu={(e) => {           //마우스 오른쪽 버튼을 눌렀을 때 메뉴가 열리는 이벤트
               e.preventDefault();         //메뉴가 열리는 것을 방지
                onDecrement();              // => 마우스 오른쪽 클릭 : 감소
            }}
            onDoubleClick={onSetColor}
            style={{ backgroundColor: color }}
        >
            {number}
        </div>
    );

};

//{ number, color, onIncrement, onDecrement, onSetColor 
Counter.propTypes = {
    number: PropTypes.number,
    color: PropTypes.string,
    onIncrement: PropTypes.func,
    onDecrement: PropTypes.func,
    onSetColor: PropTypes.func
};

Counter.defaultProps = {
    number: 0,
    color: 'black',
    onIncrement: () => console.warn('onIncrement function is not defined'),
    onDecrement: () => console.warn('onDecrement function is not defined'),
    onSetColor: () => console.warn('onSetColor function is not defined')
}
export default Counter;

onContextMenu() : 마우스 오른쪽 버튼을 눌렀을때 메뉴가 열리는 이벤트

e.preventDefault() : 메뉴가 열리는 것을 방지

/*src/components/Counter.css*/
.counter {
    width: 10rem;
    height: 10rem;
    border-radius: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 1rem;
    color: white;
    font-size: 3rem;
    cursor: pointer;
    user-select: none; /* 더블클릭을 통한 텍스트 선택 불가능 */
    transition: background-color 0.2s;
}

App.js에 Counter.js 렌더링

//src/containers/App.js
import React, {Component} from 'react';
import Counter from '../components/Counter';

class App extends Component {
    render() {
        return (
            <div>
                <Counter />
            </div>
        );
    }
}

export default App;

 

 

4. action types 생성

//src/actions/ActionTypes.js

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const SET_COLOR = 'SET_COLOR';

 

5. action creator function 생성

//src/actoins/index.js

import * as types from './ActionTypes';

export const increment = () => ({
    type:types.INCREMENT
});

export const decrement = () => ({
    type:types.DECREMENT
    
});

export const setColor = (color) => ({
    type:types.SET_COLOR,
    color:color
});

 

6. reducer 생성

//src/reducers/reducerColor.js

import * as types from '../actions/ActionTypes';

const initialState = {
    color: 'red'
};

const reducerColor = (state = initialState.color, action) => {
    switch (action.type) {
        case types.SET_COLOR:
            return {  color: action.color };
        default:
            return state;
    }
}

export default reducerColor;

 

//src/reducers/reducerNumber.js

import * as types from '../actions/ActionTypes';

const initialState = {
    number:1
}

const reducerNumber = (state=initialState, action) =>{
    switch (action.type) {
        case types.INCREMENT:
            return {number:state.number+1};
        case types.DECREMENT:
            return {number:state.number-1};
        default:
            return state;
    }
};

export default reducerNumber;

 

//src/reducers/index.js

import reducerNumber from './reducerNumber';
import reducerColor from './reducerColor';
import {combineReducers} from 'redux';

//리듀서에 의해 만들어진 state는 어플리케이션 state와 합쳐지고
const reducers = combineReducers({
    numberData : reducerNumber,
    colorData : reducerColor
});
//새로 만들어진 state는 컨테이너로 전달


export default reducers;

 

7. 스토어 생성

//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './containers/App';

//creating store
import { createStore } from 'redux';
import reducers from './reducers';
const store = createStore(reducers, window.devToolsExtension && window.devToolsExtension());

ReactDOM.render(<App />,ndocument.getElementById('root'));

- 리엑트에서 스토어를 생성할 때는 보통 프로젝트의 엔트리 포인트인 src/index.js파일에서 만든다

 

8. Provider 컴포넌트로 App.js와 store 연동

//src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './containers/App';

//creating store
import { createStore } from 'redux';
import reducers from './reducers';
const store = createStore(reducers, window.devToolsExtension && window.devToolsExtension());

//react와 store 연동
import { Provider } from 'react-redux'; 

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root')
);

- Provider는 react-redux 라이브러리에 내장된 리액트 app에 store를 연동시키는 컴포넌트

 

9. CounterContainer 컴포넌트 생성

//src/containers/CounterContainer.js
import Counter from '../components/Counter';
import * as actions from '../actions';
import {connect} from 'react-redux'; // subscrie

//랜덤하게 색깔 선택
export const getRandomColor = () => {
    const colors = ['green', 'yellow', 'blue'];
    const random = Math.floor(Math.random()*3);
    return colors[random];
};

const mapStateToProps = (state) => ({
    number:state.numberData.number,
    color:state.colorData.color
});

const mapDispatchToProps = (dispatch) => ({
    onIncrement: ()=>dispatch(actions.increment()),
    onDecrement: ()=>dispatch(actions.decrement()),
    onSetColor:() => {
        const color = getRandomColor();
        dispatch(actions.setColor(color));
    }
});

//Promte Counter from a component to a container
// => it needs to know about dispatch methods, which would be available as props
const CounterContainer = connect(mapStateToProps, mapDispatchToProps)(Counter);

export default CounterContainer;

- 리덕스의 buit-in-function인 subscribe 대신 react-redux 라이브러리의 connect(mapStateToProps, mapDispatchToProps, mergeProps) 메서드를 사용하여 컴포넌트를 스토어에 연결

mapStateToProps : state값을 파라미터로 받아 props로 전달하는 함수, props로 사용할 객체를 반환

mapDispatchToProps : 액션을 스토어에게 전달하는 dispatch함수를 파라미터로 받고 props에 연결하는 함수. 전달하는 함수들을 객체 안에 넣어서 반환

mergeProps :  state와 dispatch가 동시에 필요한 함수를 props에 전달할 때 사용

//src/containers.App.js
import React, {Component} from 'react';
import CounterContainer from './CounterContainer';

class App extends Component {
    render() {
        return (
            <div>
                <CounterContainer />
            </div>
        );
    }
}

export default App;
Posted by yongminLEE