꼬박 이틀이 걸렸다...
redux-toolkit을 이해하는 데에 하루가 걸렸고 이 둘을 연결하는 데에 하루가 걸렸다.
여기에 typescript가 같이 들어와버리니 아직 매끄럽게 이해하지는 못했다.
그래도 크롬 개발자 도구를 보니 연결이 된 것을 알 수 있었다.
참고:
- https://xploitdev.com/dev/next-redux-toolkit/
- next-redux-wrapper github 예시
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import rootReducer from '../reducers/index.reducer';
const makeStore = () =>
configureStore({
reducer: rootReducer,
// middleware: getDefaultMiddleware => getDefaultMiddleware(),
// devTools,
// preloadedState,
// enhancers:
});
export type AppStore = ReturnType<typeof makeStore>;
export type AppState = ReturnType<AppStore['getState']>;
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, AppState, unknown, Action>;
export default createWrapper(makeStore);
github 예시를 참고하여 작성하였다.
다만 redux-toolkit 문서를 읽어보면, createSlice보다 createReducer가 action-creator 분리 등의 이점이 더욱 있는 것 같았기에, 나는 createReducer를 사용하였다.
wrapper를 만들어 export 해주었다.
아래의
export type
으로 시작하는 세줄은 현재로서는 잘 모르겠다.
우선 위의 두 개는 store type과 state type을 내보내는 것으로 보이고,
AppThunk는 toolkit에 redux-thunk가 내장되어 있기 때문에 그때 사용하는 것으로 보인다.
import { createReducer } from '@reduxjs/toolkit';
import { ADD } from '../actions/exercise.actions';
interface StringState {
value: string;
}
const initialState = { value: 'hi' } as StringState;
const exerciseReducer = createReducer(initialState, builder => {
builder.addCase(ADD, (state, action) => {
state.value = action.payload;
});
});
export default exerciseReducer;
우선 state는 {value : 'hi'}를 기본으로 해서 만들어 보았다.
createReducer를 이용해서 앞으로 다른 행동들에 대응하도록 만들 예정이다.
import { createAction } from '@reduxjs/toolkit';
export const ADD = createAction<string>('string/hi');
export const hi = createAction<string>('string/ho');
action들은 기본적으로 다른 디렉토리에서 만들 예정이다. 기본 redux에서는
const ADD_TODO = 'ADD_TODO'
같은 것을 계속 만들어가면서 reducer의 분기와 action 객체 생성에 넣어주었었는데, toolkit에서는 그럴 필요가 없어졌다.여기서 만든 action creater들은 각 컴포넌트에서 dispatch와 함께 사용하면 된다.
import { combineReducers, AnyAction } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
import { AppState } from '../store/store';
import exerciseReducer from './exercise.reducer';
const rootReducer = combineReducers({
exerciseReducer,
});
export const reducer = (state: AppState, action: AnyAction) => {
if (action.type === HYDRATE) {
return {
...state,
...action.payload,
};
}
return rootReducer(state, action);
};
export default rootReducer;
여기를 작성하는 데에 애를 좀 먹었다. (최대한 혼자 작성해보고 싶었다.)
HYDRATE를 연결해주는 부분에 있어서는 보통 redux에서 reducer를 만드는 문법을 적용하였다.
위의 깃허브 예시에는 createSlice를 만들고, 그 slice마다 extra-reducer에 hydrate에 대응하게 각각 코드를 작성한 것으로 되어있는데, 읽는 입장에서는 괜시리 코드양만 늘어나는 결과만 가져오는 것으로 보인다.
AppState는 store.ts에서 정의한 type을 가져왔다. 또한 AnyAction은
export interface AnyAction extends Action { // Allows any extra properties to be defined in an action [extraProps: string] : any }
이런 모양의 type이다.
따라서 모든 모양의 action이 들어올 수 있다는 의미로AnyAction
타입을 지정하였다.
import React from 'react';
import type { AppProps } from 'next/app';
import wrapper from '../redux/store/store';
const App = ({ Component }: AppProps) => {
return <Component />;
};
export default wrapper.withRedux(App);
마지막으로 store 파일에서 정의한 wrapper를 이용해서 _app.js
를 감싸주었다.
감사합니다