Last active
February 20, 2020 08:44
-
-
Save isBatak/883b5ae602146848c04f8544a33c368b to your computer and use it in GitHub Desktop.
Next.js withDatx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// utils/env.ts | |
export const isBrowser = !!(typeof window !== 'undefined' && window.document && window.document.createElement); | |
export const isServer = !isBrowser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// stores/index.ts | |
import { isServer } from '../utils/env'; | |
import { AppStore } from './AppStore'; | |
const __NEXT_MOBX_STORE__ = 'store'; | |
export function getOrInitializeStore(initialState?: object): AppStore { | |
// Always make a new store if on the server, otherwise the state is shared between requests | |
if (isServer) { | |
return new AppStore(initialState); | |
} | |
// This will be true if the page constructor is called on the client | |
if (!window[__NEXT_MOBX_STORE__]) { | |
window[__NEXT_MOBX_STORE__] = new AppStore(initialState); | |
} | |
return window[__NEXT_MOBX_STORE__]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { useObserver } from 'mobx-react'; | |
import { withDatx, useStore } from '../lib/with-datx'; | |
const IndexPage = () => { | |
const store = useStore(); | |
const data = store.findAll(...); | |
return useObserver(() => ( | |
<div> | |
{data.map(...)} | |
</div> | |
) | |
} | |
IndexPage.getInitialProps = async ({ store }) => { | |
await store.fetchAll(...); | |
return {} | |
} | |
export default withDatx(IndexPage) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// lib/with-datx.tsx | |
import { NextPage, NextPageContext } from 'next'; | |
import App from 'next/app'; | |
import React from 'react'; | |
import { getOrInitializeStore } from '../stores'; | |
import { AppStore } from '../stores/AppStore'; | |
import '../utils/datx-config'; | |
const StoreContext = React.createContext<AppStore | null>(null) | |
export const useStore = () => { | |
const store = React.useContext(storeContext) | |
if (!store) { | |
// this is especially useful in TypeScript so you don't need to be checking for null all the time | |
throw new Error('useStore must be used within a withDatx hoc.') | |
} | |
return store | |
} | |
// Extend NextPage type's context with 'store' (which is of type AppStore) | |
export type NextPageWithDatx<P = {}, IP = P> = NextPage<P, IP> & { | |
getInitialProps?: (ctx: NextPageContext & { store: AppStore }) => Promise<IP>; | |
}; | |
// Inspired by https://github.com/zeit/next.js/tree/canary/examples/with-redux | |
export const withDatx = (PageComponent: NextPageWithDatx, options: { ssr: boolean } = { ssr: true }) => { | |
const WithDatx = ({ initialMobxState, ...props }) => { | |
const store: AppStore = getOrInitializeStore(initialMobxState); | |
return ( | |
<StoreContext.Provider value={store}> | |
<PageComponent {...props} /> | |
</StoreContext.Provider> | |
); | |
}; | |
// Make sure people don't use this HOC on _app.js level | |
if (process.env.NODE_ENV !== 'production') { | |
const isAppHoc = PageComponent.prototype instanceof App; | |
if (isAppHoc) { | |
throw new Error('The withDatx HOC only works with PageComponents'); | |
} | |
} | |
// Set the correct displayName in development | |
if (process.env.NODE_ENV !== 'production') { | |
const displayName = PageComponent.displayName || PageComponent.name || 'Component'; | |
WithDatx.displayName = `withDatx(${displayName})`; | |
} | |
const { ssr = true } = options; | |
if (ssr || PageComponent.getInitialProps) { | |
WithDatx.getInitialProps = async (context: NextPageContext) => { | |
// Get or Create the store with `undefined` as initialState | |
// This allows you to set a custom default initialState | |
const store = getOrInitializeStore(); | |
// Provide the store to getInitialProps of pages | |
// Run getInitialProps from HOCed PageComponent | |
const pageProps: any = | |
typeof PageComponent.getInitialProps === 'function' | |
? await PageComponent.getInitialProps({ ...context, store }) | |
: {}; | |
// Pass props to PageComponent | |
return { | |
...pageProps, | |
initialMobxState: store.snapshot, | |
}; | |
}; | |
} | |
return WithDatx; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment