2019. 8. 29. 21:50

0. 디렉터리 생성

 

1. action types 수정

//src/actions/ActionTypes.js

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

 

2. action creators 수정

//src/actions/index.js

import * as types from './ActionTypes';

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

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

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

export const create = (color) => ({
    type:types.CREATE,
    color
});

export const remove = () => ({
    type:types.REMOVE
});

 

3. reducers 수정

//src/reducers/index.js

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

const initialState = { counters: [{ color: 'black', number: 1 }] };


const reducers = (state = initialState, action) => {

    const {counters} = state;

    switch (action.type) {
        case types.CREATE:
            return {
                counters:[...counters,{color:action.color, number:1}]
            };
        case types.REMOVE:
            return {
                counters:counters.slice(0,counters.length-1)
            };
        case types.INCREMENT:
            return { 
                counters:[...counters.slice(0,action.index),
                {
                    ...counters[action.index],
                    number:counters[action.index].number+1
                },
                ...counters.slice(action.index+1, counters.length)
                ]
            };
        case types.DECREMENT:
            return { 
                counters:[...counters.slice(0,action.index),
                {
                    ...counters[action.index],
                    number:counters[action.index].number-1
                },
                ...counters.slice(action.index+1, counters.length)
                ]
            };
        case types.SET_COLOR:
            return { 
                counters:[...counters.slice(0,action.index),
                {
                    ...counters[action.index],
                    color:action.color
                },
                ...counters.slice(action.index+1, counters.length)
                ]
            };
        default:
            return state;
    }
}


 export default reducers;

 

4. Button 컴포넌트 생성

//src/components/Button.js

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

const Button = ({onCreate, onRemove}) => {
    return (
        <div className="Button">
            <div className="btn add" onClick={onCreate}>CREATE</div>
            <div className="btn rm" onClick={onRemove}>REMOVE</div>
        </div>
    );
};

Button.propTpes = {
    onCreate:PropTypes.func,
    onRemove:PropTypes.func
};

Button.defaultProps = {
    onCreate: () => console.warn("onCreate is not defined")
}

export default Button;

 

/* src/components/Button.css */
.Button {
    display:flex;
}

.Button .btn{
    flex:1;
    display:flex;
    align-items: center;
    justify-content: center;
    height: 3rem;
    color: white;
    font-size: 1rem;
    cursor: pointer;
}

.Button .add{
    background: green;
}

.Button .add:hover{
    background: yellow;
}

.Button .rm{
    background: red;
}

.Button .rm:hover{
    background: yellow;
}

 

5. CounterList 컴포넌트 생성

//src/components/CounterList.js

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

const CounterList = ({ counters, onIncrement, onDecrement, onSetColor }) => {
    const counterList = counters.map((counter, i) => (
        <Counter
            key={i}
            index={i}
            {...counter}
            onIncrement={onIncrement}
            onDecrement={onDecrement}
            onSetColor={onSetColor} 
        />
    ));

    return (
        <div className="counterList">
            {counterList}
        </div>
    );
};

CounterList.propTypes = {
    onIncrement: PropTypes.func,
    onDecrement: PropTypes.func,
    onSetColor: PropTypes.func
};

Counter.defaultProps = {
   counters:[],
    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 CounterList;

 

6. Counter 컴포넌트 수정

//src/components/Counter.js

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


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

};

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

Counter.defaultProps = {
    index:0,
    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;

 

7. getRandomColor 라이브러리 생성

// src/lib/getRandomColor.js

const getRandomColor = () => {
    const colors = ['green', 'yellow', 'blue'];
    const random = Math.floor(Math.random() * 3);
    return colors[random];
};

export default getRandomColor;

 

8. CounterContainer 삭제, CounterListContainer 생성

// src/containers/CounterListContainer.js

import CounterList from '../components/CounterList';
import * as actions from '../actions';
import {connect} from 'react-redux';
import getRandomColor from '../lib/getRandomColor';

const mapStateToProps = (state) => ({
    counters:state.counters
});

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

const CounterListContainer = connect(mapStateToProps, mapDispatchToProps)(CounterList);
export default CounterListContainer;

 

9. App 컨테이너 컴포넌트 수정

// src/containers/App.js

import React, {Component} from 'react';
import CounterListContainer from './CounterListContainer';
import Button from '../components/Button';

import {connect} from 'react-redux';
import * as actions from '../actions';
import getRandomColor from '../lib/getRandomColor';

class App extends Component {
    render() {
        const {onCreate, onRemove} = this.props;
        return (
            <div className="App">
                <Button
                    onCreate={onCreate}
                    onRemove={onRemove}
                />
                <CounterListContainer />
            </div>
        );
    }
};

const mapDispatchToProps = (dispatch) => ({
    onCreate: ()=>dispatch(actions.create(getRandomColor())),
    onRemove:()=>dispatch(actions.remove())
});

export default connect(null,mapDispatchToProps)(App);

- AppContainer 안에서 App과 redux를 connect하지 않고 App 컴포넌트에서 바로 연결

- store.state값을 필요로 하지 않으므로 connect()의 ampStateToProps는 null로 설정

'Javascript > projects' 카테고리의 다른 글

blog 1-1  (0) 2019.09.05
Todo-list (5) : redux 적용  (0) 2019.09.01
Counter (1) : 카운터 만들기  (0) 2019.08.29
Todo-list (4) : 리렌더링 최적화 하기  (0) 2019.08.29
Todo-list (3) : 데이터 추가, 수정, 삭제  (0) 2019.08.29
Posted by yongminLEE