library :
yarn add redux react-redux next-redux-wrapper
yarn add @types/react-redux -D
react-redux
: 일반 리액트 앱에서 사용되는 라이브러리
next-redux-wrapper
: next.js 프로젝트에 필요한 wrapper api
@types/react-redux -D
: 리덕스 전용 타입스크립트 타입
// "__NEXT_REDUX_WRAPPER_HYDRATE__" 리듀서를 추가
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
if (state.count) nextState.count = state.count;
return nextState;
}
return rootReducer(state, action);
};
chrome Redux DevTool
화면을 보면 리덕스 스토어 생성 (@@INIT
) 이후 __NEXT_REDUX_WRAPPER_HYDRATE__
인 리듀서를 추가하게 되는걸 볼 수 있다.
HYDRATE
: 서버에서 처음 생성된 리덕스 스토어를 클라이언트에서 사용할 수 있도록 주입
하는 역할
action.type === HYDRATE
: 처음 생성된 후 상태를 주입할 때 실행되는 함수
hydration
이 끝나고 나면 직접 설정한 rootReducer를 반환한다
// middleware
const bindMiddleware = (middleware: any) => {
if (process.env.NODE_ENV != "production") {
const { composeWithDevTools } = require("redux-devtools-extension");
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const initStore = () => {
return createStore(reducer, bindMiddleware([]));
};
process.env.NODE_ENV != "production"
: redux 의 환경 상태에 따라 "redux-devtools-extension"
미들웨어를 적용할지 지정
yarn dev
next.js의 디벨롭먼트 환굥은 "development"
// store type
export type RootState = ReturnType<typeof rootReducer>;
스토어의 타입을 리듀서로부터 얻을 수 있음
useSelector((store: RootState) => {})
과 같은 방식으로 사용됨
store/index.ts
:
import { HYDRATE, createWrapper } from "next-redux-wrapper"
...
const initStore = () => {
return createStore(reducer, bindMiddleware([]));
}
export const wrapper = createWrapper(initStore)
createWrapper
로 감싸준다import type { AppProps } from "next/app";
import { wrapper } from "../store";
const app = ({ Component, pageProps }: AppProps) => {
return (
<>
<GlobalStyle />
<Component {...pageProps} />
</>
);
};
export default wrapper.withRedux(app);
wrapper.withRedux()
함수로 감싼다pages/index.tsx
:
...
// redux-wrapper 7.0 이후의 방식
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async () => {
console.log(store);
// store 파라미터 내장 함수
// {
// dispatch: [Function: dispatch],
// subscribe: [Function: subscribe],
// getState: [Function: getState],
// replaceReducer: [Function: replaceReducer],
// '@@observable': [Function: observable]
// }
try {
const { data } = await getTodosAPI();
store.dispatch(todosSliceActions.setTodo(data));
return { props: { todos: data } };
} catch (e) {
return { props: { todos: [] } };
}
}
);
// 구버전 방식
// export const getServerSideProps = wrapper.getServerSideProps(
// async ({ store }) => {
// console.log(store);
// try {
// const { data } = await getTodosAPI();
// return { props: { todos: data } };
// } catch (e) {
// return { props: { todos: [] } };
// }
// }
// );