전역 state를 관리.
action이 일어나면 dispatch가 reducer에 action을 전달하여 state를 업데이트
(action.type, payload) 전달
1. action -------------------------------> dispatch
(action.type, payload) 전달
2. dispatch -----------------------------> reducer
3. reducer : 설정해둔 switch문에서 action.type에 따라 state를 업데이트
Ducks 패턴이란
- 리덕스를 사용하기 위해서는
리덕스의 구성 요소 (config, module)를 먼저 설정하고 준비해야 하는데
구성 요소인module
을 각각의 개발자가
각각의 형태로 만들면 협업에 어려움이 생긴다.
그렇기에 구성 요소를 준비할 때 패턴에 따라 준비하고
협업 시 지장이 없도록 하기 위해 나온 패턴이다.
Ducks 패턴의 조건
- Reducer 함수를
export default
한다.- Action creator 함수를
export
한다.- Action type은
app/reducer/ACTION_TYPE
으로 한다.
(외부 라이브러리로서 사용될 경우 또는 외부 라이브러리가 필요로 할 경우에는 UPPER_SNAKE_CASE 로만 작성해도 괜찮다.)
Ducks 패턴을 왜 사용하는가
- 하나의 state를 다루는 하나의 module에
reducer
,action type
,action creator
가 모두 존재하도록 하여
나중에 혼란이 없도록 하는 것.
npm i redux react-redux
yarn add redux react-redux
config 폴더
, module 폴더
를 생성configStore.js
생성<configStore.js>
import {legacy_createStore as createStore} from 'redux';
import {combineReducer} from 'redux';
// 인자는 module 파일에서 만든 reducer가 들어감. ({key:value 형태로})
const rootReducer = combineReducer({});
// 모든 reducer 뭉탱이를 가진 store
const store = createStore(rootReducer);
export default store
import { legacy_createStore as createStore } 하는 이유
- createStore로 import 해도 아무 문제 없다. (Redux가 공식적으로 발표)
다만 그냥 createStore를 하면 이렇게creatStore()취소선이 있다.
<Provider>
에 store를 전달한다.<index.js>
import store from '/src/config/configStore'
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
export default 리듀서이름
counter 모듈 예시
<counter.js>
// 초기 상태값 (state 같은 것)
const initialState = {
number: 0,
};
// counter 리듀서 (state 변경 함수 같은 것)
const counter = (state = initialState, action) => {
switch (action.type) { // switch문을 통해 type에 따라 작동할 코드를 설정
case "ADD_COUNT":
return { number: state.number + 1 } // initialState의 값을 업데이트
default:
return state;
}
};
// export default 한다.
export default counter;
<configStore.js>
// key와 value의 이름이 동일하면
// 한 번만 적어도 됨 {counter: counter} -> {counter}
const rootReducer = combineReducer({counter});
useSelector()
로 원하는 state를 가져온다.useDispatch()
state를 업데이트 하는 reducer에 타입 객체를 전달한다.사용 예시
<CounterApp.jsx>
const counter = useSelector((state) => state.counter); // state는 객체로 온다.
const dispatch = useDispatch();
// 버튼을 눌렀을 때 counter state의 number를 1 증가하는 함수
const addCount = () => {
dispatch({type:"ADD_COUNT"}}); // dispatch()로 type 객체 전달
}
<h1>{counter.number}</h1> // 0
<button onClick={addCount}>카운트 증가</button> // counter.number가 1씩 잘 증가한다.
Context API와 달리 사전설정이 까다롭고
눈과 손에 안 익어서 익숙해질 때까지는
메모를 여러 번 들춰볼 거 같다.
그리고 여기서 끝이 아니라
Action type
, Action creator
설명이 아직 남았다.
이 두 가지는 human error
를 줄이고
유지 / 보수에 편리함을 위한 방법인데
내일 적어봐야겠다..