getInitialProps 를 사용하므로, SSR 정적 최적화는 없다. 그리고, 서버사이드에서 리덕스를 사용해야할 이유가 왜 있는지 자꾸 생각을 해보게 된다. 크로스 플랫폼 환경에서 새로고침 시 정보를 저장하기 위해서, 것도 인증정보 때문이라면 때문이라면 redux-persist 를 써도 될텐데. 왜 구지 express-session 을 쓸까? 알아갈 수록 궁금증이 늘어가네.
https://github.com/bmealhouse/next-redux-saga
next-redux-wrapper 에 의해 생긴 리덕스 스토어를 사용한다고 함.
preloadedState: The initial state. You may optionally specify it to hydrate the state from the server in universal apps, or to restore a previously serialized user session. If you produced reducer with combineReducers, this must be a plain object with the same shape as the keys passed to it. Otherwise, you are free to pass anything that your reducer can understand.
https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootReducer from './root-reducer'
import rootSaga from './root-saga'
function configureStore(preloadedState, {isServer, req = null}) {
/**
* Recreate the stdChannel (saga middleware) with every context.
*/
const sagaMiddleware = createSagaMiddleware()
/**
* Since Next.js does server-side rendering, you are REQUIRED to pass
* `preloadedState` when creating the store.
*/
const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(sagaMiddleware)
)
/**
* next-redux-saga 는 `getInitialProps` 처리 중에 스토어에 붙기 위해 `sagaTask` 에 의존함.
* 이건, 클라이언트에 결과를 보내기 전에 rootSaga 를 await 하는데 사용됨.
* However, next-redux-wrapper creates two server-side stores per request:
* One before `getInitialProps` and one before SSR (see issue #62 for details).
* 서버사이드에서, 우리는 `getInitialProps` 중에서만 rootSaga 를 실행함:
*/
// isServer 는 SSR 빌드 타임을 이야기 하는 거 같음.
// 즉 빌드 타임이 아니라 Request Time 이면 사가태스크 실행해서 store 에 넣으라고 하는 듯.
if (req || !isServer) {
store.sagaTask = sagaMiddleware.run(rootSaga)
}
return store
}
export default configureStore
next.js 기본 App 작동을 커스텀화하자. 즉 모든 페이지(컴포넌트)를 받아서 Provier 로 감싸고, 무조건 위에서 만든 store 의 state 를 제공하도록 함.
import React from 'react'
import {Provider} from 'react-redux'
import App, {Container} from 'next/app'
import withRedux from 'next-redux-wrapper'
import withReduxSaga from 'next-redux-saga'
import configureStore from './configure-store'
function MyApp({Component, pageProps, store}) {
return (
<Container>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</Container>
)
}
MyApp.getInitialProps = async ({Component, ctx}) => {
let pageProps = {}
// SSR 때 data population 하면
if (Component.getInitialProps) {
// ctx (store 가 들어있음) 를 주입
pageProps = await Component.getInitialProps(ctx)
}
return {pageProps}
}
export default withRedux(configureStore)(withReduxSaga(ExampleApp))
요건 그냥 ContextAPI 임.
import React, {Component} from 'react'
import {connect} from 'react-redux'
class ExamplePage extends Component {
// _app.js getInitialProps 훅에서 주입된 ctx 에서 store 추출 후
static async getInitialProps({store}) {
// 필요한 비동기 요청 실행
store.dispatch({type: 'SOME_ASYNC_ACTION_REQUEST'})
return {staticData: 'Hello world!'}
}
render() {
return <div>{this.props.staticData}</div>
}
}
export default connect(state => state)(ExamplePage)