본문 바로가기

react-redux practice

1. react-redux를 사용하는 기본적인 index.js파일 세팅

 

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reducer from './js/reducers'
import { Provider } from 'react-redux'
import { applyMiddleware, createStore } from 'redux';
import { logger } from 'redux-logger'

// const store = createStore(reducer, applyMiddleware(logger)) // 디버깅에 유용한 logger를 사용하고 싶을 때
const store = createStore(reducer)

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

 

여기서 reducer는 state와 action을 파라미터로 받는 state를 컨트롤할 수 있는 함수이다. (직접 정의해야함)

랜더링될 때 Redux 컴포넌트인 <Provider>에 store를 설정해주면 그 하위 컴포넌트들에 따로 props로 전달해주지 않아도 connect될 때 store에 접근할 수 있게 해준다.

 

2. reducer함수 파일

 

import {
    INPUT,
    ADD,
    COMPLETE,
    DELETES,
    OPTION,
    MOUSEOVER,
    MOUSEOUT
} from '../types'

const initialState = {
    listAll: [],
    listCompleted: [],
    listActive: [],
    option: 'All',
    content: '',
    optionList: [{fontWeight: null, cursor: null}, {fontWeight: null, cursor: null}, {fontWeight: null, cursor: null}]
    
}

const reducer = (state=initialState, action) => {
    switch(action.type) {
        case INPUT:
            return Object.assign({}, state, {
                content: action.content
            })
        case ADD:
            let temp = state.listAll.slice()
            temp.push([state.content, {textDecoration: 'none'}])
            return Object.assign({}, state, {
                listAll: temp,
                content: ''
            })
        case COMPLETE:
            let temp2 = state.listAll.slice()
            let tempContent = temp2[action.index][0]
            temp2.splice(action.index, 1, [tempContent, {textDecoration: 'line-through'}])
            return Object.assign({}, state, {
                listAll: temp2
            })
        case DELETES:
            let temp3 = state.listAll.slice()
            temp3.splice(action.index, 1)
            return Object.assign({}, state, {
                listAll: temp3
            })
        case OPTION:
            let temp4 = state.listAll.slice()
            let tempCompleted = []
            let tempActive = []
            if(action.optionType === 'Completed') {
                tempCompleted = temp4.filter(function(x) {
                    return x[1].textDecoration === 'line-through'
                })
            } else if (action.optionType === 'Active') {
                tempActive = temp4.filter(function(x) {
                    return x[1].textDecoration === 'none'
                })
            }
            return Object.assign({}, state, {
                option: action.optionType,
                listCompleted: tempCompleted,
                listActive: tempActive
            })
        case MOUSEOVER:
            let tempArr = state.optionList.slice()
            tempArr[action.target] = {fontWeight: 'bold', cursor: 'pointer'}
            console.log(tempArr)
            return Object.assign({}, state, {
                optionList: tempArr
            })
        case MOUSEOUT:
            let tempArr2 = state.optionList.slice()
            tempArr2[action.target] = {fontWeight: null, cursor: null}
            return Object.assign({}, state, {
                optionList: tempArr2
            })
        default:
            return state
    }
}

export default reducer;

 

createStore의 파라미터로 들어가는 reducer함수는 state와 action을 파라미터로 갖는다.

여기서 state가 react에서의 state라고 생각해도 무방하며, 처음에 react에서 this.state를 정의하듯이 초기값들을 설정해주어야 한다.

action은 type이라는 속성을 가지고 있어야 하고, 그 type에 따라 다른 동작을 할 수 있도록 reducer함수를 작성해야한다.

reducer함수는 setState와 비슷하게 객체형식의 데이터를 리턴해주어야 하는데, 이때 보통 원래의 state를 가져와 필요한 값들만 변경할 수 있도록 Object.assign을 이용한다.

 

3. action 파일

 

export const INPUT = 'INPUT'
export const ADD = 'ADD';
export const COMPLETE = 'COMPLETE';
export const DELETES = 'DELETES';
export const OPTION = 'OPTION'
export const MOUSEOVER = 'MOUSEOVER'
export const MOUSEOUT = 'MOUSEOUT'

export function input(str) {
    return {
        type: INPUT,
        content: str
    }
}

export function add() {
    return {
        type: ADD,
        
    }
}

export function complete(key) {
    return {
        type: COMPLETE,
        index: key
    }
}

export function deletes(key) {
    return {
        type: DELETES,
        index: key
    }
}

export function option(str) {
    return {
        type: OPTION,
        optionType: str,
    }
}

export function mouseOver(index) {
    return {
        type: MOUSEOVER,
        target: index
    }
}

export function mouseOut(index) {
    return {
        type: MOUSEOUT,
        target: index
    }
}

 

각각의 action들은 type이라는 속성을 가지고있어야 하며, 필요에 따라 파라미터로 어떤 값을 받아 reducer함수에게 전달해줄 수 있다.

예를들어 mouseOut이라는 액션의 target값에 접근하고 싶다면 reducer함수 내에서

 

const reducer (state, action) {
    switch(action.type) {
        case MOUSEOUT:
            let temp = action.target
            return ................
    }
}

 

위와같이 action.target으로 값을 가져올 수 있다.

 

5. 각각의 컴포넌트 파일

 

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { option } from '../types'
import { mouseOver } from '../types'
import { mouseOut } from '../types'

class Option extends Component {
    render() {
        return (
            <div className='option-box'>
                <span onMouseOver={() => this.props.mouseOver(0)} onMouseOut={() => this.props.mouseOut(0)} style={this.props.style[0]} onClick={() => this.props.changeOption('All')}>All</span>
                <span className='slash'>/</span>
                <span onMouseOver={() => this.props.mouseOver(1)} onMouseOut={() => this.props.mouseOut(1)} style={this.props.style[1]} onClick={() => this.props.changeOption('Completed')}>Completed</span>
                <span className='slash'>/</span>
                <span onMouseOver={() => this.props.mouseOver(2)} onMouseOut={() => this.props.mouseOut(2)} style={this.props.style[2]} onClick={() => this.props.changeOption('Active')}>Active</span>
            </div>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        style: state.optionList
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        changeOption: (str) => dispatch(option(str)),
        mouseOver: (index) => dispatch(mouseOver(index)),
        mouseOut: (index) => dispatch(mouseOut(index))
    }
}

Option = connect(mapStateToProps, mapDispatchToProps)(Option)

export default Option

 

위의 코드에서는 connect라는 개념이 등장하는데, connect는 react-redux의 내장 API로서, React Component를 Redux Store에 연결해주는 역할을 한다. 

파라미터로 받는 mapStateToProps는 store의 state값을 가져오고 싶을 때,

mapDispatchToProps는 특정 이벤트로 인해 store의 state값을 변경해주고 싶을때 사용하는것으로 생각된다.

이렇게 두 함수를 정의하고 connect를 통해 연결시켜주면

컴포넌트에서 this.props로 위의 함수에서 리턴한 객체들의 속성값들을 사용할 수 있다.

 

첫 화면을 랜더링하는것에서는 react와 큰차이가 없는것 같지만 어떤 이벤트가 발생했을때의 흐름을 살펴보면,

이벤트가 발생했을 때 dispatch('action함수')가 실행되면 reducer함수에서 그 action함수 타입에 해당하는 코드들이 실행된다.

이렇게 state가 바뀌게 되고 그 바뀐부분만을 화면에 다시 랜더링해주게 되는 것으로 생각된다.

 

react에서는 state값을 가져오거나, 이벤트가 발생했을때 state를 변경해주기 위해서는 최상위 컴포넌트에서부터 state값이나 이벤트시 실행되어야 하는 함수를 계속해서 props로 내려주어야 하지만 react-redux에서는 store의 state나 작성해놓은 함수를 직접 가져와 사용할 수 있는 장점이 있는 것 같다.

'' 카테고리의 다른 글

React-practice  (0) 2020.01.28
CORS  (0) 2020.01.17
HTTP  (0) 2020.01.15