Preview
reducer
, saga
, store
, type
설정
store/rootReducer.ts
reducer
여러 reducer 들을 combineReducers로 하나로 묶음
const reducer = combineReducers({
[FETCH_STATUS]: fetchStatusReducer,
[USER]: userReducer,
[POST]: postReducer,
[HASHTAG]: hashtagReducer,
});
RootState[type]
redux에서 state 타입을 항상 써야 하기 때문에 import 하지 않고 global 영역으로 declare 선언
declare global {
type RootState = ReturnType<typeof reducer>;
}
rootReducer
next-redux-wrapper
로 인해 action.type에서 HYDRATE 타입이 호출되기 때문에 구분자 설정
HYDRATE 타입일 때, 이전 state는 초기화가 됨
const rootReducer = (state: RootState | undefined, action: AnyAction) => {
switch (action.type) {
case HYDRATE:
return action.payload;
default: {
return reducer(state, action);
}
}
};
hooks/useAppRedux.ts
useAppSelector
reducer에서 반환된 데이터는 모두 RootState 범주 안에서 이루어지기 때문에, RootState 타입을 가진 useSelector hooks를 커스텀
import { TypedUseSelectorHook, useSelector } from 'react-redux';
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
useAppDispatch
dispatch
의 type은 anyAction
으로 적용 되어, 그냥 일반 useDispatch
에서 나온 타입과 동일하기 때문에, 사용자 지정 타입을 제외 했다.
store/rootSaga.ts
여러 saga들이 모듈화로 관리 되기 때문에, 모든 saga들이 action을 watching 해야하기 때문에 전체 관리 rootSaga를 만들어줌
export default function* rootSaga() {
yield all([fork(userSaga), fork(postSaga), fork(hashtagSaga)]);
}
store/configStore.ts
CSR 설정이었으면, makeStore를 만들 필요 없이 configureStore를 하게되지만, SSR 설정으로 유저가 요청할 때 마다 redux store를 새로(configureStore) 생성하게 되므로 redux store가 여러 개가 될 수 있다.
makeStore로 하나의 스토어를 다루도록 설정하여, front-server에서 redux-store가 여러 개 생성되는것을 방지한다.
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { createWrapper } from 'next-redux-wrapper';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './rootReducer';
import rootSaga from './sagas';
const devMode = process.env.NODE_ENV === 'development';
// Next.js를 사용하게 되면 유저가 요청할 때 마다
// redux store를 새로(configureStore) 생성하게 되므로 redux store가 여러 개가 될 수 있다.
// makeStore로 하나의 스토어를 다루도록 설정
const makeStore: MakeStore = () => {
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
// async Saga를 위해 resolve, reject를 반환해서 에러가 생기는 이슈로,
// serializableCheck 미들웨어를 사용안 함
serializableCheck: false,
}).concat(sagaMiddleware),
devTools: devMode,
});
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
const wrapper = createWrapper(makeStore, {
debug: devMode,
});
export default wrapper;
pages/_app.tsx
코드 레이아웃 영역이다. header에 들어갈 공통 영역 및, 전체 글로벌 설정 영역을 여기서 다룬다고 생각하면 된다.
여기서 store/configStore.ts에서 생성한 wrapper를 HOC 패턴으로 App을 인자로 전달한다.
이 설정이 없으면, HYDRATE action.type을 못 받게 되므로, SSR을 할 수 없게 된다.
import wrapper from '@modules/store/configStore';
// ...
export default wrapper.withRedux(App);
typings/redux.d.ts
redux는 타입스크립트가 내장되어 있지만, helper 함수 혹은, next-redux-wrapper로 추가 타입을 지정 해야할때, 전역으로 관리되는 type을 하나의 파일에서 지정한다.
import { ThunkAction } from '@reduxjs/toolkit';
import 'redux';
import { Task } from 'redux-saga';
// Next Redux Toolkit Saga를 사용할때는
// configureStore에서 강제로 sagaTask를 만들어주기 위함
declare module 'redux' {
export interface Store {
sagaTask: Task;
}
export interface Dispatch<A extends Action = AnyAction> {
<T extends ThunkAction<any, any, any, any>>(action: T): T extends ThunkAction<infer K, any, any, any> ? K : never;
}
}
code view
'urTweet' 카테고리의 다른 글
[modules] React + Data fetch handling (0) | 2021.11.22 |
---|---|
[setting] Atomic Design? (0) | 2021.11.20 |
[setting] React 컴포넌트 분리와 폴더 구조화 (4) | 2021.11.13 |
[helper] Redux + Redux-saga (0) | 2021.11.12 |
[setting] Next + Typescript Environment Setup (0) | 2021.11.11 |