π§ 리λμ€ κ°λ°μλꡬ μ μ©νκΈ°
https://react.vlpt.us/redux/06-redux-devtools.html
3. 리λμ€ μ¬μ©ν μ€λΉνκΈ°
import { createStore } from 'redux'; // μ€ν μ΄ μμ±
<exercise.js>
import { createStore } from 'redux';
// createStoreλ μ€ν μ΄λ₯Ό λ§λ€μ΄μ£Όλ ν¨μμ
λλ€.
// 리μ‘νΈ νλ‘μ νΈμμλ λ¨ νλμ μ€ν μ΄λ₯Ό λ§λλλ€.
/* 리λμ€μμ κ΄λ¦¬ ν μν μ μ */
const initialState = {
counter: 0,
text: '',
list: []
};
/* μ‘μ
νμ
μ μ */
// μ‘μ
νμ
μ μ£Όλ‘ λλ¬Έμλ‘ μμ±ν©λλ€.
const INCREASE = 'INCREASE';
const DECREASE = 'DECREASE';
const CHANGE_TEXT = 'CHANGE_TEXT';
const ADD_TO_LIST = 'ADD_TO_LIST';
/* μ‘μ
μμ±ν¨μ μ μ */
// μ‘μ
μμ±ν¨μλ μ£Όλ‘ camelCase λ‘ μμ±ν©λλ€.
function increase() {
return {
type: INCREASE // μ‘μ
κ°μ²΄μλ type κ°μ΄ νμμ
λλ€.
};
}
// νμ΄ν ν¨μλ‘ μμ±νλ κ²μ΄ λμ± μ½λκ° κ°λ¨νκΈ°μ,
// μ΄λ κ² μ°λ κ²μ μΆμ²ν©λλ€.
const decrease = () => ({
type: DECREASE
});
const changeText = text => ({
type: CHANGE_TEXT,
text // μ‘μ
μμλ type μΈμ μΆκ°μ μΈ νλλ₯Ό λ§μλλ‘ λ£μ μ μμ΅λλ€.
});
const addToList = item => ({
type: ADD_TO_LIST,
item
});
/* 리λμ λ§λ€κΈ° */
// μ μ‘μ
μμ±ν¨μλ€μ ν΅ν΄ λ§λ€μ΄μ§ κ°μ²΄λ€μ μ°Έμ‘°νμ¬
// μλ‘μ΄ μνλ₯Ό λ§λλ ν¨μλ₯Ό λ§λ€μ΄λ΄
μλ€.
// μ£Όμ: 리λμμμλ λΆλ³μ±μ κΌ μ§μΌμ€μΌ ν©λλ€!
function reducer(state = initialState, action) {
// state μ μ΄κΉκ°μ initialState λ‘ μ§μ νμ΅λλ€.
switch (action.type) {
case INCREASE:
return {
...state,
counter: state.counter + 1
};
case DECREASE:
return {
...state,
counter: state.counter - 1
};
case CHANGE_TEXT:
return {
...state,
text: action.text
};
case ADD_TO_LIST:
return {
...state,
list: state.list.concat(action.item)
};
default:
return state;
}
}
/* μ€ν μ΄ λ§λ€κΈ° */
const store = createStore(reducer);
console.log(store.getState()); // νμ¬ store μμ λ€μ΄μλ μνλ₯Ό μ‘°νν©λλ€.
// μ€ν μ΄μμ λ€μ΄μλ μνκ° λ°λ λ λ§λ€ νΈμΆλλ listener ν¨μ
const listener = () => {
const state = store.getState();
console.log(state);
};
const unsubscribe = store.subscribe(listener);
// ꡬλ
μ ν΄μ νκ³ μΆμ λλ unsubscribe() λ₯Ό νΈμΆνλ©΄ λ©λλ€.
// μ‘μ
λ€μ λμ€ν¨μΉ ν΄λ΄
μλ€.
store.dispatch(increase());
store.dispatch(decrease());
store.dispatch(changeText('μλ
νμΈμ'));
store.dispatch(addToList({ id: 1, text: 'μμ°' }));
4. 리λμ€ λͺ¨λ λ§λ€κΈ°
src/modules/
리λμ€ λͺ¨λ λ§λ€κΈ°
리λμ€ λͺ¨λ? λ€μ νλͺ©λ€μ΄ λͺ¨λ λ€μ΄μλ μλ°μ€ν¬λ¦½νΈ νμΌ
- μ‘μ νμ
- μ‘μ μμ±ν¨μ
- 리λμ
<modules/counter.js>
/* μ‘μ
νμ
λ§λ€κΈ° */
// Ducks ν¨ν΄μ λ°λ₯Όλ μ‘μ
μ μ΄λ¦μ μ λμ¬λ₯Ό λ£μ΄μ£ΌμΈμ.
// μ΄λ κ² νλ©΄ λ€λ₯Έ λͺ¨λκ³Ό μ‘μ
μ΄λ¦μ΄ μ€λ³΅λλ κ²μ λ°©μ§ ν μ μμ΅λλ€.
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
/* μ‘μ
μμ±ν¨μ λ§λ€κΈ° */
// μ‘μ
μμ±ν¨μλ₯Ό λ§λ€κ³ export ν€μλλ₯Ό μ¬μ©ν΄μ λ΄λ³΄λ΄μ£ΌμΈμ.
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
/* μ΄κΈ° μν μ μΈ */
const initialState = {
number: 0,
diff: 1
};
/* 리λμ μ μΈ */
// 리λμλ export default λ‘ λ΄λ³΄λ΄μ£ΌμΈμ.
export default function counter(state = initialState, action) {
switch (action.type) {
case SET_DIFF:
return {
...state,
diff: action.diff
};
case INCREASE:
return {
...state,
number: state.number + state.diff
};
case DECREASE:
return {
...state,
number: state.number - state.diff
};
default:
return state;
}
}
λ£¨νΈ λ¦¬λμ λ§λ€κΈ°
λ£¨νΈ λ¦¬λμ? 리λμλ€μ ν©μ³μ λ§λλ λ£¨νΈ λ¦¬λμ
import { combineReducers } from 'redux';
<modules/index.js> => λ£¨νΈ λ¦¬λμ λ§λ€κΈ°
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({
counter,
todos
});
export default rootReducer;
<src/index.js> => μ€ν μ΄ λ§λ€κΈ°
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import rootReducer from './modules';
const store = createStore(rootReducer); // μ€ν μ΄λ₯Ό λ§λλλ€.
console.log(store.getState()); // μ€ν μ΄μ μνλ₯Ό νμΈν΄λ΄
μλ€.
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
리μ‘νΈ νλ‘μ νΈμ 리λμ€ μ μ©νκΈ°
$ yarn add react-redux
<src/index.js>
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
const store = createStore(rootReducer); // μ€ν μ΄λ₯Ό λ§λλλ€.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
=> Providerλ‘ storeλ₯Ό λ£μ΄μ Appμ κ°μΈκ² λλ©΄ μ°λ¦¬κ° λ λλ§νλ κ·Έ μ΄λ€ μ»΄ν¬λνΈλμ§ λ¦¬λμ€ μ€ν μ΄μ μ κ·Όν μ μλ€.
5. ꡬννκΈ°
ν리μ ν μ΄μ λ μ»΄ν¬λνΈ λ§λ€κΈ°
src/components/
νλ μ ν μ΄μ λ μ»΄ν¬λνΈ? 리λμ€ μ€ν μ΄μ μ§μ μ μΌλ‘ μ κ·Όνμ§ μκ³ νμν κ° λλ ν¨μλ₯Ό propsλ‘λ§ λ°μμμ μ¬μ©νλ μ»΄ν¬λνΈ
=> μ£Όλ‘ UIλ₯Ό μ μΈνλ κ²μ μ§μ€νλ©°, νμν κ°λ€μ΄λ ν¨μλ propsλ‘ λ°μμμ μ¬μ©νλ ννλ‘ κ΅¬ννλ€.
<components/Counter.js>
import React from 'react';
function Counter({ number, diff, onIncrease, onDecrease, onSetDiff }) {
const onChange = e => {
// e.target.value μ νμ
μ λ¬Έμμ΄μ΄κΈ° λλ¬Έμ μ«μλ‘ λ³νν΄μ£Όμ΄μΌ ν©λλ€.
onSetDiff(parseInt(e.target.value, 10));
};
return (
<div>
<h1>{number}</h1>
<div>
<input type="number" value={diff} min="1" onChange={onChange} />
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
}
export default Counter;
컨ν μ΄λ μ»΄ν¬λνΈ λ§λ€κΈ°
src/containers/
컨ν μ΄λ μ»΄ν¬λνΈ? 리λμ€ μ€ν μ΄μ μνλ₯Ό μ‘°ννκ±°λ, μ‘μ μ λμ€ν¨μΉ ν μ μλ μ»΄ν¬λνΈλ₯Ό μλ―Ένλ€.
HTML νκ·Έλ€μ μ¬μ©νμ§ μκ³ λ€λ₯Έ ν리μ ν μ΄μ λ μ»΄ν¬λνΈλ€μ λΆλ¬μμ μ¬μ©νλ€.
<containers/CounterContainer.js>
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';
function CounterContainer() {
// useSelectorλ 리λμ€ μ€ν μ΄μ μνλ₯Ό μ‘°ννλ Hookμ
λλ€.
// stateμ κ°μ store.getState() ν¨μλ₯Ό νΈμΆνμ λ λνλλ κ²°κ³Όλ¬Όκ³Ό λμΌν©λλ€.
const { number, diff } = useSelector(state => ({
number: state.counter.number,
diff: state.counter.diff
}));
// useDispatch λ 리λμ€ μ€ν μ΄μ dispatch λ₯Ό ν¨μμμ μ¬μ© ν μ μκ² ν΄μ£Όλ Hook μ
λλ€.
const dispatch = useDispatch();
// κ° μ‘μ
λ€μ λμ€ν¨μΉνλ ν¨μλ€μ λ§λμΈμ
const onIncrease = () => dispatch(increase());
const onDecrease = () => dispatch(decrease());
const onSetDiff = diff => dispatch(setDiff(diff));
return (
<Counter
// μνμ
number={number}
diff={diff}
// μ‘μ
μ λμ€ν¨μΉ νλ ν¨μλ€μ propsλ‘ λ£μ΄μ€λλ€.
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetDiff={onSetDiff}
/>
);
}
export default CounterContainer;
<App.js>
import React from 'react';
import CounterContainer from './containers/CounterContainer';
function App() {
return (
<div>
<CounterContainer />
</div>
);
}
export default App;
8. useSelector μ΅μ ν
<container/CounterContainer.js>
const { number, diff } = useSelector(state => ({
number: state.counter.number,
diff: state.counter.diff
}));
=> useSeletor Hookμ ν΅ν΄ λ§€λ² λ λλ§ λ λλ§λ€ μλ‘μ΄ κ°μ²΄(number, diff)λ₯Ό λ§λλ κ²μ΄κΈ° λλ¬Έμ μνκ° λ°λμλμ§ νμΈν μ μμ΄μ λλΉ λ λλ§μ΄ μ΄λ£¨μ΄μ§κ² λλ€.
μ΅μ ν λ°©λ²! useSelectorλ₯Ό μ¬λ¬λ² μ¬μ©νλ€.
const number = useSelector(state => state.counter.number);
const diff = useSelector(state => state.counter.diff);
=> μ΄λ κ² νλ©΄ ν΄λΉ κ°λ€ νλλΌλ λ°λμμ λλ§ μ»΄ν¬λνΈκ° 리λ λλ§λλ€.
9. connect ν¨μ
useSelector, useDispatchλ λΉμ·
=> ν΄λμ€ν μ»΄ν¬λνΈμμλ Hook μ¬μ© λͺ»νλκΉ connect ν¨μ μ¬μ©ν¨..
// useSelectorλ 리λμ€ μ€ν μ΄μ μνλ₯Ό μ‘°ννλ Hookμ
λλ€.
// stateμ κ°μ store.getState() ν¨μλ₯Ό νΈμΆνμ λ λνλλ κ²°κ³Όλ¬Όκ³Ό λμΌν©λλ€.
const { number, diff } = useSelector(state => ({
number: state.counter.number,
diff: state.counter.diff
}));
// useDispatch λ 리λμ€ μ€ν μ΄μ dispatch λ₯Ό ν¨μμμ μ¬μ© ν μ μκ² ν΄μ£Όλ Hook μ
λλ€.
const dispatch = useDispatch();
// κ° μ‘μ
λ€μ λμ€ν¨μΉνλ ν¨μλ€μ λ§λμΈμ
const onIncrease = () => dispatch(increase());
const onDecrease = () => dispatch(decrease());
const onSetDiff = diff => dispatch(setDiff(diff));
connectλ HOCμ΄λ€.
HOC(Higher-Order Component): μ»΄ν¬λνΈλ₯Ό νΉμ ν¨μλ‘ κ°μΈμ νΉμ κ° λλ ν¨μλ₯Ό propsλ‘ λ°μμμ μ¬μ©ν μ μκ² ν΄μ£Όλ ν¨ν΄
const enhance = withState('counter', 'setCounter', 0)
const Counter = enhance(({ counter, setCounter }) =>
<div>
Count: {counter}
<button onClick={() => setCounter(n => n + 1)}>Increment</button>
<button onClick={() => setCounter(n => n - 1)}>Decrement</button>
</div>
)
connect μ¬μ©ν΄λ³΄κΈ°
<containers/CounterContainer.js>
import React from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';
function CounterContainer({ number, diff, onIncrease, onDecrease, onSetDiff }) {
return (
<Counter
// μνμ
number={number}
diff={diff}
// μ‘μ
μ λμ€ν¨μΉ νλ ν¨μλ€μ propsλ‘ λ£μ΄μ€λλ€.
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetDiff={onSetDiff}
/>
);
}
// mapStateToProps λ 리λμ€ μ€ν μ΄μ μνλ₯Ό μ‘°νν΄μ μ΄λ€ κ²λ€μ props λ‘ λ£μ΄μ€μ§ μ μν©λλ€.
// νμ¬ λ¦¬λμ€ μνλ₯Ό νλΌλ―Έν°λ‘ λ°μμ΅λλ€.
const mapStateToProps = state => ({
number: state.counter.number,
diff: state.counter.diff
});
// mapDispatchToProps λ μ‘μ
μ λμ€ν¨μΉνλ ν¨μλ₯Ό λ§λ€μ΄μ propsλ‘ λ£μ΄μ€λλ€.
// dispatch λ₯Ό νλΌλ―Έν°λ‘ λ°μμ΅λλ€.
const mapDispatchToProps = dispatch => ({
onIncrease: () => dispatch(increase()),
onDecrease: () => dispatch(decrease()),
onSetDiff: diff => dispatch(setDiff(diff))
});
// connect ν¨μμλ mapStateToProps, mapDispatchToProps λ₯Ό μΈμλ‘ λ£μ΄μ£ΌμΈμ.
export default connect(
mapStateToProps,
mapDispatchToProps
)(CounterContainer);
/* μ μ½λλ λ€μκ³Ό λμΌν©λλ€.
const enhance = connect(mapStateToProps, mapDispatchToProps);
export defualt enhance(CounterContainer);
*/
'βοΈ > React' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
React 곡λΆνκΈ°(7) - redux-thunk of 리λμ€ λ―Έλ€μ¨μ΄ (0) | 2021.11.09 |
---|---|
React 곡λΆνκΈ°(7) - 리λμ€ λ―Έλ€μ¨μ΄ (0) | 2021.11.09 |
React 곡λΆνκΈ°(5) - 리μ‘νΈ λΌμ°ν° (0) | 2021.11.05 |
React 곡λΆνκΈ°(4) - API μ°λνκΈ° (0) | 2021.11.05 |
React 곡λΆνκΈ°(2) - styled-components (0) | 2021.10.15 |