Next.js 에서 SSR 서버사이드렌더링로 특정 페이지를 제대로 렌더링해서 클라이언트로 보내주고 싶었다.
따라서 아래 2가지를 구현해야 했다
데이터 fetch 가 완료될 때까지 기다리는 함수를 개인적으로 추가했다
type Options = {
actionType: DataSagaActionType
key: string
}
export const waitDuringLoading = async (store: Store<RootState, AnyAction>, {actionType, key}: Options) => {
while (true){
await (async () => new Promise(resolve => setTimeout(resolve, 100)))()
const isLoading = store.getState().data[actionType][key].status === DataSagaStatus.LOADING
if (!isLoading) break;
}
}
import {waitDuringLoading} from // ...
// ...
export const getServerSideProps = wrapper.getServerSideProps(store => async ({req, res, ...etc}) => {
const GET_PUBLIC_TASKS_KEY = ""
const GET_GOALS_BY_IDS_KEY = ""
// 액션 디스패치 하기
store.dispatch(dataActionCreators[DataActionType.GET_PUBLIC_TASKS]({
author: undefined,
key: GET_PUBLIC_TASKS_KEY,
startTime: new Date("1999-11-11"),
endTime: new Date("2222-11-11"),
}))
// 데이터 fetch 완료될때까지 기다리기
await waitDuringLoading(store, {actionType: DataActionType.GET_PUBLIC_TASKS, key: GET_PUBLIC_TASKS_KEY})
// state 에서 값 읽기
const tasksGoal = store.getState().data[DataActionType.GET_PUBLIC_TASKS][GET_PUBLIC_TASKS_KEY].data?.map(item => item.goal)
// ...
return ({
props: {}
})
});
next-redux-wrapper 에서 작업해주는 HYDRATE 라는 액션을 이용해서
getStaticProps
나 getServerSideProps
가 있는 페이지로 처음 접속 또는 이동할 때,import merge, {Options as MergeOptions} from "deepmerge"
import {HYDRATE} from "next-redux-wrapper";
import {combineReducers} from "redux";
import {dataReducer, State as DataState, initialState as dataInitialState} from "./data";
import {navigationReducer, State as NavigationState, initialState as navigationInitialState} from "./navigation";
const combinedReducer = combineReducers({
data: dataReducer,
navigation: navigationReducer,
});
export type RootState = {
data: DataState;
navigation: NavigationState;
}
const initialRootState: RootState = {
data: dataInitialState,
navigation: navigationInitialState
}
const arrayMerge: MergeOptions["arrayMerge"] = (previousArray, incomingArray, options) => {
const resultArray: typeof previousArray = [...previousArray]
incomingArray.forEach((incomingItem) => {
const prevItemIndex = previousArray.findIndex(previousItem => previousItem.id === incomingItem.id)
if (prevItemIndex !== -1){
resultArray[prevItemIndex] = merge(resultArray[prevItemIndex], incomingItem, options)
}
else {
resultArray.push(incomingItem)
}
})
return resultArray
}
const rootReducer = (previousClientState = initialRootState, action: any) => {
if (action.type === HYDRATE) {
const incomingServerState = action.payload as RootState
const nextState: RootState = {
navigation: previousClientState.navigation,
data: merge(previousClientState.data, incomingServerState.data, {arrayMerge})
}
return nextState;
} else {
return combinedReducer(previousClientState, action);
}
};
export default rootReducer;