react redux - MoonGyeongHyeon/React_Study GitHub Wiki
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๋ฅผ ๋๊ฒผ๋ค.
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์ ์ ๊ทผํ ์ ์๋๋ก ํด์ค๋ค.