react redux - MoonGyeongHyeon/React_Study GitHub Wiki

React - React-Redux

react-redux๋ฅผ ์ด์šฉํ•˜์—ฌ input box๋กœ ๊ฐ’์„ ์ž…๋ ฅ๋ฐ›๊ณ  +, - ๋ฒ„ํŠผ์œผ๋กœ ์ฆ๊ฐ์‹œํ‚ค๋Š” ์˜ˆ์ œ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด๋„๋ก ํ•˜์ž.

๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

src
โ”œโ”€โ”€ actions
โ”‚        โ””โ”€โ”€ index.js
โ”œโ”€โ”€ components
โ”‚        โ”œโ”€โ”€ App.js
โ”‚        โ”œโ”€โ”€ Buttons.js
โ”‚        โ”œโ”€โ”€ Counter.js
โ”‚        โ””โ”€โ”€ Option.js
โ”œโ”€โ”€ index.js
โ””โ”€โ”€ reducers
    โ””โ”€โ”€ index.js

actions/index.js

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const SET_DIFF = 'SET_DIFF';

export function increment() {
    return {
        type: INCREMENT
    };
}

export function decrement() {
    return {
        type: DECREMENT
    };
}

export function setDiff(value) {
    return {
        type: SET_DIFF,
        diff: value
    };
}

INCREMENT: ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

DECREMENT: ๊ฐ’์„ ๊ฐ์†Œ์‹œํ‚จ๋‹ค.

SET_DIFF: ๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆด ๋•Œ ๋”ํ•˜๊ฑฐ๋‚˜ ๋บ„ ๊ฐ’์„ ์„ค์ •ํ•œ๋‹ค.

reducers/index.js

import { INCREMENT, DECREMENT, SET_DIFF } from '../actions';
import { combineReducers } from 'redux';

const counterInitialState = {
    value: 0,
    diff: 1
};

const counter = (state = counterInitialState, action) => {
    switch(action.type) {
        case INCREMENT:
            return Object.assign({}, state, {
                value: state.value + state.diff
            });
        case DECREMENT:
            return Object.assign({}, state, {
                value: state.value - state.diff
            });
        case SET_DIFF:
            return Object.assign({}, state, {
                diff: action.diff
            });
        default:
            return state;
    }
};


const extra = (state = { value: 'this_is_extra_reducer' }, action) => {
    switch(action.type) {
        default:
            return state;
    }
}

const counterApp = combineReducers({
    counter,
    extra
});

export default counterApp;

combineReducers ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ reducer๋ฅผ ํ•˜๋‚˜๋กœ ํ•ฉ์น  ๋•Œ ์‚ฌ์šฉ๋˜๋Š” redux ๋‚ด์žฅ ๋ฉ”์†Œ๋“œ์ด๋‹ค. ์ธ์ž๋กœ reducer๋ฅผ ๋ชจ์•„๋†“์€ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค.

๋‚ด๋ถ€ ๊ตฌํ˜„ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

const counterApp = ( state = { }, action ) => {
    return {
        counter: counter(state.counter, action),
        extra: extra(state.extra, action)
    }
}

์œ„ ์ฝ”๋“œ์˜ reducer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ store๋ฅผ ๋งŒ๋“ค ๊ฒฝ์šฐ, store์˜ state ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

{
    counter: { value: 0, diff: 1 }
    extra: { value: 'this_is_extra_reducer' }
}

reducer๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ž‘์„ฑํ•  ๋•, ์„œ๋กœ ์ง์ ‘์ ์ธ ๊ด€๊ณ„๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, INCREMENT์™€ DECREMENT์—์„œ diff ๊ฐ’์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ SET_DIFF๋ฅผ ๋‹ค๋ฅธ reducer์— ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค.

๋งŒ์•ฝ combineReducers๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ๊ฐ reducer์— ๋‹ค๋ฅธ key๊ฐ’์„ ์ฃผ๊ณ ์ž ํ•œ๋‹ค๋ฉด,

const counterApp = combineReducers({
    a: counter,
    b: extra
});

์œ„์™€ ๊ฐ™์ด ํ‘œํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

components/Counter.js

import React from 'react';
import { connect } from 'react-redux';

class Counter extends React.Component {
    render() {
        return (
            <h1>VALUE: { this.props.value }</h1>
        );
    }
}

let mapStateToProps = (state) => {
    return {
        value: state.counter.value
    };
}

Counter = connect(mapStateToProps)(Counter);

export default Counter;
  • connect API

    connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

    connect๋Š” react-redux์˜ ๋‚ด์žฅ API์ด๋‹ค. ์ด ํ•จ์ˆ˜๋Š” React Component๋ฅผ Redux Store์— ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.

    ์ด ํ•จ์ˆ˜๋Š” ํŠน์ • ์ปดํฌ๋„ŒํŠธ ํด๋ž˜์Šค์˜ props๋ฅผ store์˜ ๋ฐ์ดํ„ฐ์— ์—ฐ๊ฒฐ์‹œ์ผœ์ฃผ๋Š” ๋˜ ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. ๋ฆฌํ„ด๋œ ํ•จ์ˆ˜์— ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ธ์ˆ˜๋กœ ๋„ฃ์–ด ์‹คํ–‰ํ•˜๋ฉด, ๊ธฐ๋ณธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

    • mapStateToProps(state, [ownProps]): (Function) store์˜ state๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ props๋กœ ๋งคํ•‘์‹œ์ผœ์ค€๋‹ค. ownProps ์ธ์ž๊ฐ€ ๋ช…์‹œ๋  ๊ฒฝ์šฐ, ์ด๋ฅผ ํ†ตํ•ด ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ปดํฌ๋„ŒํŠธ์˜ props ๊ฐ’์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

    • mapDispatchToProps(dispatch, [ownProps]): (Function or Object) ์ปดํฌ๋„ŒํŠธ์˜ ํ•จ์ˆ˜ props๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ, ์ž„์˜๋กœ ์ง€์ •ํ•œ action์„ dispatchํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค.

      โ€‹

Counter = connect(mapStateToProps)(Counter); ๋Š” ๋จผ์ €, connect ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ mapStateToProps ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ–ˆ๋‹ค. ์ฆ‰, store์— ์กด์žฌํ•˜๋Š” 'counter' reducer์˜ value ์†์„ฑ์˜ ๊ฐ’์„ props๋กœ ์ฐธ์กฐํ•˜๋„๋ก ํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ ์šฉ์‹œํ‚ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ Counter๋กœ ์„ค์ •ํ–ˆ๋‹ค. ๊ธฐ์กด์˜ ์กด์žฌํ•˜๋Š” Counter๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ, ์ƒˆ๋กœ์šด Counter ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

components/Buttons.js

import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from '../actions';

class Buttons extends React.Component {
    render() {
        return (
            <div>
                <button type="button"
                        onClick={ this.props.onIncrement }>
                        +
                </button>
                <button type="button"
                        onClick={ this.props.onDecrement }>
                        -
                </button>
            </div>
        )
    }
}


let mapDispatchToProps = (dispatch) => {
    return {
        onIncrement: () => dispatch(increment()),
        onDecrement: () => dispatch(decrement())
    }
}

Buttons = connect(undefined, mapDispatchToProps)(Buttons);

export default Buttons;

Buttons = connect(undefined, mapDispatchToProps)(Buttons); ๋Š”, ์ธ์ž๋กœ mapDispatchToProps ํ•จ์ˆ˜๋ฅผ ๋„˜๊ฒผ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋Š” ์˜๋„๋Š” ํŠน์ • props(onIncrement, onDecrement) ๋ฅผ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ, ์ž„์˜์˜ action(increment(), decrement()) ์„ dispatch(์ธ์ž dispatch)ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. ์ฐธ๊ณ ๋กœ, mapStateToProps ์ธ์ž๋Š” ํ•„์š” ์—†์œผ๋ฏ€๋กœ undefined๋ฅผ ๋„˜๊ฒผ๋‹ค.

components/Option.js

import React from 'react';
import { connect } from 'react-redux';
import { setDiff } from '../actions';

class Option extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            diff: '1'
        }


        this.onChangeDiff = this.onChangeDiff.bind(this);
    }

    render() {
        return (
            <div>
                <input type="text" value={ this.state.diff } onChange={this.onChangeDiff}></input>
            </div>
        );
    }

    onChangeDiff(e) {

        if(isNaN(e.target.value))
            return;

        this.setState({ diff: e.target.value });

        if(e.target.value=='') {
            this.setState({ diff: '0' });
        }

        this.props.onUpdateDiff(parseInt(e.target.value));

    }
}

let mapDispatchToProps = (dispatch) => {
    return {
        onUpdateDiff: (value) => dispatch(setDiff(value))
    };
}

Option = connect(undefined, mapDispatchToProps)(Option);

export default Option;

components/App.js

import React from 'react';
import Counter from './Counter';
import Buttons from './Buttons';
import Option from './Option';

class App extends React.Component {
    render(){
        return (
            <div style={ {textAlign: 'center'} }>
                <Counter/>
                <Option/>
                <Buttons/>
            </div>
        );
    }

}

export default App;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider  } from 'react-redux';
import App from './components/App';
import counterApp from './reducers';

const store = createStore(counterApp);
const appElement = document.getElementById('app');



ReactDOM.render(
    <Provider store = {store}>
        <App />
    </Provider>,
    appElement
);

๋ Œ๋”๋ง ์‹œ์ ์— Redux ๋‚ด์žฅ ์ปดํฌ๋„ŒํŠธ์ธ <Provider> ์— store ๋ฅผ ์„ค์ •ํ•ด์ฃผ๋ฉด ๊ทธ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ๋ณ„๋„๋กœ parent-child ๊ตฌ์กฐ๋กœ ์ „๋‹ฌํ•ด์ฃผ์ง€ ์•Š์•„๋„ connect ๋  ๋•Œ store์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ