react ducks 패턴
을 참고하여 모듈화를 적용 시키는 것 까지 해보았다.RTK
의 새로운 문법을 이용하여 기존 redux core
프로젝트를 RTK
로 전환해보고자 한다.https://www.youtube.com/watch?v=UKnLwVm9suY&t=631s&ab_channel=코딩알려주는누나
를 참고했습니다.
// modules/counter.ts (기존 모듈)
export const PLUS_COUNTER = 'counter/PLUS_COUNTER';
export const MINUS_COUNTER = 'counter/MINUS_COUNTER';
export const CLEAR_COUNTER = 'counter/CLEAR_COUNTER';
export const plusCounter = () => {
return {
type: PLUS_COUNTER,
};
};
export const minusCounter = () => {
return {
type: MINUS_COUNTER,
};
};
export const clearCounter = () => {
return {
type: CLEAR_COUNTER,
};
};
const initialState = {
count: 0,
};
function counterReducer(state = initialState, action: { type: string }) {
switch (action.type) {
case PLUS_COUNTER:
return {
...state,
count: state.count + 1,
};
case MINUS_COUNTER:
return {
...state,
count: state.count - 1,
};
case CLEAR_COUNTER:
return {
...state,
count: 0,
};
default:
return state;
}
}
export default counterReducer;
createSlice()
는 리듀서와 액션을 쉽게 만들 수 있는 메소드이다.name
, initialState
, reducers
라는 값이 존재해야한다.name
은 해당 slice
의 이름을 결정한다. 여기서 정해진 이름은 slice
가 유니크한 action
의 값을 만드는데 사용된다.initialState
는 최초 상태를 결정한다.reducers
는 함수로 이루어진 객체를 받고, 실질적으로 reducer
를 만드는 역할을 한다.switch-case
문을 사용하는 경우 spread
로 기존 state
를 가져온 뒤 값을 변경해야 했지만, createSlice()
에서는 해당 과정을 생략 할 수 있다.createSlice()
가 유니크한 action
을 자동으로 생성하므로, action 생성함수
, type
을 작성하지 않아도 된다.// modules/counter.ts (createSlice 사용)
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
count: 0,
};
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
plusCounter(state) {
state.count++;
},
minusCounter(state) {
state.count--;
},
clearCounter(state) {
state.count = 0;
},
},
});
export const counterActions = counterSlice.actions;
export default counterSlice.reducer;
counterSlice.reducer
에 해당 reducer
값이 담기기 때문에 export default counterSlice.reducer
를 사용하여 리듀서를 내보내고 store
에서 사용 할 수 있다.counterSlice.actions
에 action
이 담기므로, counterActions
에 counterSlice.actions
를 담아 내보내어 action
을 dispatch
할 때 사용 할 수 있다.createStore()
를 대체하는 store
생성 방법이다.createStore()
에 combineReducers
, composeWithDevTools
, thunk
, applyMiddleware
가 포함되어 있다고 생각하면 좋을 것 같다.combineReducers()
를 사용하여 만들어진 rootReducer
를 createStore()
에 전달하는 방식을 사용했다.// modules/index.ts
import { combineReducers } from '@reduxjs/toolkit';
import arrayReducer from './array';
import counterReducer from './counter';
import objectReducer from './object';
import textReducer from './text';
const rootReducer = combineReducers({
arrayReducer,
counterReducer,
objectReducer,
textReducer,
});
export default rootReducer;
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { legacy_createStore } from '@reduxjs/toolkit';
import rootReducer from './modules';
// combineReducer 로 만든 rootReducer 를 가져온다.
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const store = legacy_createStore(rootReducer);
root.render(
<Provider store={store}>
<App />
</Provider>
);
configureStore()
는 combineReducers()
의 기능을 포함하는데, 함수의 인자로 객체를 전달하고, reducer
라는 키값을 가진 객체에 만들어진 reducer
들을 전달 하면된다.// index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import arrayReducer from '././modules/array';
import counterReducer from '././modules/counter';
import objectReducer from '././modules/object';
import textReducer from '././modules/text';
// combineReducer 를 사용하지 않아도되므로, 리듀서를 직접가져온다.
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const store = configureStore({
reducer: {
arrayReducer,
counterReducer,
objectReducer,
textReducer,
// combineReducer 처럼 동작한다.
},
});
root.render(
<Provider store={store}>
<App />
</Provider>
);
reducer
에서 actions
를 내보냈는데, 이는 함수(action)를 가진 객체의 형태를 띈다.dispatch
를 사용하되, 객체에서 메소드를 사용하듯 사용하면 된다.import { counterActions } from '../modules/counter';
// 따로 action 을 하나하나 불러오는 것이 아니라,
// createSlice의 값을 이용한다.
function Counter(): JSX.Element {
const state = useSelector(
(state: { counterReducer: { count: number } }) => state
);
const dispatch = useDispatch();
return (
<>
<div className="title">Counter</div>
<div className="count">{state.counterReducer.count}</div>
<div className="buttons">
<button onClick={() => dispatch(counterActions.plusCounter())}>
더하기
</button>
<button onClick={() => dispatch(counterActions.minusCounter())}>
빼기
</button>
<button onClick={() => dispatch(counterActions.clearCounter())}>
초기화
</button>
</div>
</>
);
}