Skip to content

Commit

Permalink
Improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
HaNdTriX committed Feb 7, 2020
1 parent 495246c commit 04dffc4
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 43 deletions.
4 changes: 2 additions & 2 deletions examples/with-apollo/apolloClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { InMemoryCache } from 'apollo-cache-inmemory'
import { HttpLink } from 'apollo-link-http'
import fetch from 'isomorphic-unfetch'

export default function createApolloClient(apolloState, ctx) {
export default function createApolloClient(initialState, ctx) {
// The `ctx` will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
return new ApolloClient({
Expand All @@ -13,6 +13,6 @@ export default function createApolloClient(apolloState, ctx) {
credentials: 'same-origin', // Additional fetch() options like `credentials` or `headers`
fetch,
}),
cache: new InMemoryCache().restore(apolloState),
cache: new InMemoryCache().restore(initialState),
})
}
87 changes: 50 additions & 37 deletions examples/with-apollo/lib/apollo.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ import Head from 'next/head'
import { ApolloProvider } from '@apollo/react-hooks'
import createApolloClient from '../apolloClient'

// On the client we store the apollo client in the following variable
// this prevents the client from reinitializing between page transitions.
let globalApolloClient = null

// We expose this function so we are able to use
// apolloClient inside getStaticProps, getStaticPaths or getServerProps
export const ensureApolloClient = ctx => {
/**
* Installes the apollo client on NextPageContext
* or NextAppContext. Useful if you want to use apolloClient
* inside getStaticProps, getStaticPaths or getServerProps
* @param {NextPageContext | NextAppContext} ctx
*/
export const initOnContext = ctx => {
const inAppContext = Boolean(ctx.ctx)

// We consider installing `withApollo({ ssr: true })` on global App level
// as antipattern since it disables project wide Automatic Static Optimization.
if (process.env.NODE_ENV === 'development') {
if (inAppContext) {
console.warn(
Expand Down Expand Up @@ -41,21 +49,43 @@ export const ensureApolloClient = ctx => {
return ctx
}

/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {NormalizedCacheObject} initialState
* @param {NextPageContext} ctx
*/
const initApolloClient = (initialState, ctx) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return createApolloClient(initialState, ctx)
}

// Reuse client on the client-side
if (!globalApolloClient) {
globalApolloClient = createApolloClient(initialState, ctx)
}

return globalApolloClient
}

/**
* Creates a withApollo HOC
* that provides the apolloContext
* to a next.js Page or AppTree.
* @param {Object} withApolloOptions
* @param {Boolean} [withApolloOptions.ssr=false]
* @returns {(PageComponent: ReactNode) => ReactNode}
*/
export const withApollo = ({ ssr } = {}) => PageComponent => {
export const withApollo = ({ ssr = false } = {}) => PageComponent => {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
// Called by:
// - getDataFromTree => apolloClient
// - next.js ssr => apolloClient
// - next.js csr => apolloState
let client
if (apolloClient) {
// Happens on: getDataFromTree & next.js ssr
client = apolloClient
} else {
// Happens on: next.js csr
client = initApolloClient(apolloState, undefined)
}

Expand All @@ -70,14 +100,13 @@ export const withApollo = ({ ssr } = {}) => PageComponent => {
if (process.env.NODE_ENV !== 'production') {
const displayName =
PageComponent.displayName || PageComponent.name || 'Component'

WithApollo.displayName = `withApollo(${displayName})`
}

if (ssr || PageComponent.getInitialProps) {
WithApollo.getInitialProps = async ctx => {
const inAppContext = Boolean(ctx.ctx)
const { apolloClient } = ensureApolloClient(ctx)
const { apolloClient } = initOnContext(ctx)

// Run wrapped getInitialProps methods
let pageProps = {}
Expand All @@ -99,7 +128,8 @@ export const withApollo = ({ ssr } = {}) => PageComponent => {
// Only if dataFromTree is enabled
if (ssr && AppTree) {
try {
// Run all GraphQL queries
// Import `@apollo/react-ssr` dynamically.
// We don't want to have this in our client bundle.
const { getDataFromTree } = await import('@apollo/react-ssr')

// Since AppComponents and PageComponents have different context types
Expand All @@ -111,8 +141,11 @@ export const withApollo = ({ ssr } = {}) => PageComponent => {
props = { pageProps: { ...pageProps, apolloClient } }
}

// Takes React AppTree, determine which queries are needed to render,
// then fetche them all.
// Take the Next.js AppTree, determine which queries are needed to render,
// and fetch them. This method can be pretty slow since it renders
// your entire AppTree once for every query. Check out apollo fragments
// if you want to reduce the number of rerenders.
// https://www.apollographql.com/docs/react/data/fragments/
await getDataFromTree(<AppTree {...props} />)
} catch (error) {
// Prevent Apollo Client GraphQL errors from crashing SSR.
Expand All @@ -127,36 +160,16 @@ export const withApollo = ({ ssr } = {}) => PageComponent => {
}
}

// Extract query data from the Apollo store
const apolloState = apolloClient.cache.extract()

return {
...pageProps,
apolloState,
// Extract query data from the Apollo store
apolloState: apolloClient.cache.extract(),
// Provide the client for ssr. As soon as this payload
// gets JSON.stringified it will remove itself.
apolloClient: ctx.apolloClient,
}
}
}

return WithApollo
}

/**
* Always creates a new apollo client on the server
* Creates or reuses apollo client in the browser.
* @param {Object} initialState
*/
const initApolloClient = (initialState, ctx) => {
// Make sure to create a new client for every server-side request so that data
// isn't shared between connections (which would be bad)
if (typeof window === 'undefined') {
return createApolloClient(initialState, ctx)
}

// Reuse client on the client-side
if (!globalApolloClient) {
globalApolloClient = createApolloClient(initialState, ctx)
}

return globalApolloClient
}
4 changes: 3 additions & 1 deletion examples/with-apollo/pages/about.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import App from '../components/App'
import Header from '../components/Header'

export default () => (
const AboutPage = () => (
<App>
<Header />
<article>
Expand Down Expand Up @@ -41,3 +41,5 @@ export default () => (
</article>
</App>
)

export default AboutPage
1 change: 0 additions & 1 deletion examples/with-apollo/pages/client-only.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,4 @@ const ClientOnlyPage = props => (
</App>
)

// Disable apollo ssr fetching in favour of automatic static optimization
export default withApollo()(ClientOnlyPage)
4 changes: 2 additions & 2 deletions examples/with-apollo/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Submit from '../components/Submit'
import PostList from '../components/PostList'
import { withApollo } from '../lib/apollo'

const IndexPage = props => (
const IndexPage = () => (
<App>
<Header />
<InfoBox>
Expand All @@ -26,4 +26,4 @@ const IndexPage = props => (
</App>
)

export default withApollo()(IndexPage)
export default withApollo({ ssr: true })(IndexPage)

0 comments on commit 04dffc4

Please sign in to comment.