import App , { AppContext , AppInitialProps } from 'next/app' ; import React from 'react' ; import { Provider } from 'react-redux' ; import { Store } from 'redux' ; import { GetServerSideProps , GetServerSidePropsContext , GetStaticProps , GetStaticPropsContext , NextComponentType , NextPageContext , } from 'next' ; /**
 * Quick note on Next.js return types:
 *
 * Page.getInitialProps https://nextjs.org/docs/api-reference/data-fetching/getInitialProps
 * as-is
 *
 * App.getInitialProps: AppInitialProps https://nextjs.org/docs/advanced-features/custom-app
 * {pageProps: any}
 *
 * getStaticProps https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
 * {props: any}
 *
 * getServerSideProps https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering
 * {props: any}
 */ export const HYDRATE = '__NEXT_REDUX_WRAPPER_HYDRATE__' ; const getIsServer = ( ) => typeof window === 'undefined' ; const getDeserializedState = < S extends Store > ( initialState , { deserializeState } = { } ) => deserializeState ? deserializeState ( initialState ) : initialState ; const getSerializedState = < S extends Store > ( state , { serializeState } = { } ) => serializeState ? serializeState ( state ) : state ; export declare type MakeStore < S extends Store > = ( context : Context ) => S ; export interface InitStoreOptions < S extends Store > { makeStore : MakeStore < S > ; context : Context ; } let sharedClientStore ; const initStore = < S extends Store > ( { makeStore , context } ) : S => { const createStore = ( ) => makeStore ( context ) ; if ( getIsServer ( ) ) { const req = ( context as NextPageContext ) ?. req || ( context as AppContext ) ?. ctx ?. req ; if ( req ) { if ( ! req . __nextReduxWrapperStore ) { req . __nextReduxWrapperStore = createStore ( ) ; } return req . __nextReduxWrapperStore ; } return createStore ( ) ; } if ( ! sharedClientStore ) { sharedClientStore = createStore ( ) ; } return sharedClientStore ; } ; export const createWrapper = < S extends Store > ( makeStore , config = { } ) => { const makeProps = async ( { callback , context , addStoreToContext = false , } ) : Promise < WrapperProps > => { const store = initStore ( { context , makeStore } ) ; if ( config . debug ) { console . log ( `1. getProps created store with state` , store . getState ( ) ) ; } if ( addStoreToContext ) { if ( context . ctx ) { context . ctx . store = store ; } else { context . store = store ; } } const nextCallback = callback && callback ( store ) ; const initialProps = ( nextCallback && ( await nextCallback ( context ) ) ) || { } ; if ( config . debug ) { console . log ( `3. getProps after dispatches has store state` , store . getState ( ) ) ; } const state = store . getState ( ) ; return { initialProps , initialState : getIsServer ( ) ? getSerializedState < S > ( state , config ) : state , } ; } ; const getInitialPageProps = < P extends { } = any > ( callback ) : GetInitialPageProps < P > => async ( context ) => { if ( 'getState' in context ) { return callback && callback ( context as any ) ; } return makeProps ( { callback , context , addStoreToContext : true } ) ; } ; const getInitialAppProps = < P extends { } = any > ( callback ) : GetInitialAppProps < P > => async ( context ) => { const { initialProps , initialState } = await makeProps ( { callback , context , addStoreToContext : true , } ) ; return { ... initialProps , initialState , } ; } ; const getStaticProps = < P extends { } = any > ( callback ) : GetStaticProps < P > => async ( context ) => { const { initialProps , initialState } = await makeProps ( { callback , context , } ) ; return { ... initialProps , props : { ... initialProps . props , initialState , } , } as any ; } ; const getServerSideProps = < P extends { } = any > ( callback ) : GetServerSideProps < P > => async ( context ) => await getStaticProps ( callback as any ) ( context ) ; const withRedux = ( Component ) => { const displayName = `withRedux(${ Component . displayName || Component . name || 'Component' })` ; const hasInitialProps = 'getInitialProps' in Component ; return class Wrapper extends React . Component < any , any > { static displayName = displayName ; static getInitialProps = hasInitialProps ? Component . getInitialProps : undefined ; public store : S = null as any ; constructor ( props , context ) { super ( props , context ) ; this . hydrate ( props , context ) ; } hydrate ( { initialState , initialProps , ... props } , context ) { const initialStateFromGSPorGSSR = props ?. pageProps ?. initialState ; if ( ! this . store ) { this . store = initStore ( { makeStore , context } ) ; if ( config . debug ) { console . log ( '4. WrappedApp created new store with' , displayName , { initialState , initialStateFromGSPorGSSR , } ) ; } } if ( initialState ) { this . store . dispatch ( { type : HYDRATE , payload : getDeserializedState < S > ( initialState , config ) , } as any ) ; } if ( initialStateFromGSPorGSSR ) { this . store . dispatch ( { type : HYDRATE , payload : getDeserializedState < S > ( initialStateFromGSPorGSSR , config ) , } as any ) ; } } shouldComponentUpdate ( nextProps , nextState , nextContext ) { if ( nextProps ?. pageProps ?. initialState !== this . props ?. pageProps ?. initialState || nextProps ?. initialState !== this . props ?. initialState ) { this . hydrate ( nextProps , nextContext ) ; } return true ; } render ( ) { const { initialState , initialProps , ... props } = this . props ; let resultProps = props ; if ( initialProps && initialProps . pageProps ) { resultProps . pageProps = { ... initialProps . pageProps , ... props . pageProps , } ; } const initialStateFromGSPorGSSR = props ?. pageProps ?. initialState ; if ( initialStateFromGSPorGSSR ) { resultProps = { ... props , pageProps : { ... props . pageProps } } ; delete resultProps . pageProps . initialState ; } if ( resultProps ?. pageProps ?. initialProps ) { resultProps . pageProps = { ... resultProps . pageProps , ... resultProps . pageProps . initialProps , } ; delete resultProps . pageProps . initialProps ; } return ( < Provider store = { this . store } > < Component { ... initialProps } { ... resultProps } /> < /Provider>); } } ; } ; return { getServerSideProps , getStaticProps , getInitialAppProps , getInitialPageProps , withRedux , } ; } ; export default < S extends Store > ( makeStore , config = { } ) => { console . warn ( '/!\\ You are using legacy implementaion. Please update your code: use createWrapper() and wrapper.withRedux().' ) ; return createWrapper ( makeStore , config ) . withRedux ; } ; export type Context = NextPageContext | AppContext | GetStaticPropsContext | GetServerSidePropsContext ; export interface Config < S extends Store > { serializeState ? : ( state : ReturnType < S [ 'getState' ] > ) => any ; deserializeState ? : ( state : any ) => ReturnType < S [ 'getState' ] > ; debug ? : boolean ; } export interface WrapperProps { initialProps : any ; initialState : any ; } type GetInitialPageProps < P > = NextComponentType < NextPageContext , any , P > [ 'getInitialProps' ] ; type GetInitialAppProps < P > = ( { Component , ctx } : AppContext ) => Promise < AppInitialProps & { pageProps : P ; } > ; export type GetStaticPropsCallback < S extends Store , P > = ( store : S ) => GetStaticProps < P > ; export type GetServerSidePropsCallback < S extends Store , P > = ( store : S ) => GetServerSideProps < P > ; export type PageCallback < S extends Store , P > = ( store : S ) => GetInitialPageProps < P > ; export type AppCallback < S extends Store , P > = ( store : S ) => GetInitialAppProps < P > ; export type Callback < S extends Store , P > = GetStaticPropsCallback < S , P > | GetServerSidePropsCallback < S , P > | PageCallback < S , P > | AppCallback < S , P > ; declare module 'next' { export interface NextPageContext < S extends Store = any > { /**
         * Provided by next-redux-wrapper: The redux store
         */ store : S ; } }