From 6548b924a07f77ea225956edf19c4debf506c1d9 Mon Sep 17 00:00:00 2001 From: handtrix Date: Thu, 19 Sep 2019 16:22:14 +0200 Subject: [PATCH 1/5] Make withApollo work with _app.js components --- examples/with-apollo/lib/apollo.js | 32 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index 2c96ca96f72d0a9..6e1a1578f1b2e28 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -1,4 +1,5 @@ import React from 'react' +import App from 'next/app' import Head from 'next/head' import { ApolloProvider } from '@apollo/react-hooks' import { ApolloClient } from 'apollo-client' @@ -14,6 +15,9 @@ let globalApolloClient = null * your PageComponent via HOC pattern. */ export const withApollo = ({ ssr = true } = {}) => PageComponent => { + const isAppHoc = + PageComponent === App || PageComponent.prototype instanceof App + const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => { const client = apolloClient || initApolloClient(apolloState) return ( @@ -23,15 +27,19 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { ) } + if (process.env.NODE_ENV !== 'production') { + if (isAppHoc && ssr) { + console.warn( + 'You are using the "withApollo" HOC on "_app.js" level. Please note that this disables project wide automatic static optimization. Better wrap your PageComponents directly.' + ) + } + } + // Set the correct displayName in development if (process.env.NODE_ENV !== 'production') { const displayName = PageComponent.displayName || PageComponent.name || 'Component' - if (displayName === 'App') { - console.warn('This withApollo HOC only works with PageComponents.') - } - WithApollo.displayName = `withApollo(${displayName})` } @@ -62,14 +70,14 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { try { // Run all GraphQL queries const { getDataFromTree } = await import('@apollo/react-ssr') - await getDataFromTree( - - ) + + // Since AppComponents and PageComponents have different context types + // we need to modify their props a little. + const props = isAppHoc + ? { ...pageProps, apolloClient } + : { pageProps: { ...pageProps, apolloClient } } + + await getDataFromTree() } catch (error) { // Prevent Apollo Client GraphQL errors from crashing SSR. // Handle them in components via the data.error prop: From 8aaf38ce6e51911dd1bc81429920d7867edaa98e Mon Sep 17 00:00:00 2001 From: handtrix Date: Wed, 5 Feb 2020 14:10:10 +0100 Subject: [PATCH 2/5] Support wrapping functional _App --- examples/with-apollo/lib/apollo.js | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index 6e1a1578f1b2e28..01625d9ca7c7abd 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -1,5 +1,4 @@ import React from 'react' -import App from 'next/app' import Head from 'next/head' import { ApolloProvider } from '@apollo/react-hooks' import { ApolloClient } from 'apollo-client' @@ -15,9 +14,6 @@ let globalApolloClient = null * your PageComponent via HOC pattern. */ export const withApollo = ({ ssr = true } = {}) => PageComponent => { - const isAppHoc = - PageComponent === App || PageComponent.prototype instanceof App - const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => { const client = apolloClient || initApolloClient(apolloState) return ( @@ -27,14 +23,6 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { ) } - if (process.env.NODE_ENV !== 'production') { - if (isAppHoc && ssr) { - console.warn( - 'You are using the "withApollo" HOC on "_app.js" level. Please note that this disables project wide automatic static optimization. Better wrap your PageComponents directly.' - ) - } - } - // Set the correct displayName in development if (process.env.NODE_ENV !== 'production') { const displayName = @@ -73,10 +61,16 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { // Since AppComponents and PageComponents have different context types // we need to modify their props a little. - const props = isAppHoc - ? { ...pageProps, apolloClient } - : { pageProps: { ...pageProps, apolloClient } } - + const inAppContext = Boolean(ctx.ctx) + let props + if (inAppContext) { + props = { ...pageProps, apolloClient } + } else { + props = { pageProps: { ...pageProps, apolloClient } } + } + + // Takes React AppTree, determine which queries are needed to render, + // then fetche them all. await getDataFromTree() } catch (error) { // Prevent Apollo Client GraphQL errors from crashing SSR. From 72b9f6729b79e879fcb5e45d84b6ac528fea46c8 Mon Sep 17 00:00:00 2001 From: handtrix Date: Wed, 5 Feb 2020 14:22:24 +0100 Subject: [PATCH 3/5] Add apolloClient to NextPageContext & NextPageContext --- examples/with-apollo/lib/apollo.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index 01625d9ca7c7abd..046753e2bbe2b9d 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -34,10 +34,18 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { if (ssr || PageComponent.getInitialProps) { WithApollo.getInitialProps = async ctx => { const { AppTree } = ctx + const inAppContext = Boolean(ctx.ctx) - // Initialize ApolloClient, add it to the ctx object so - // we can use it in `PageComponent.getInitialProp`. - const apolloClient = (ctx.apolloClient = initApolloClient()) + // Initialize ApolloClient + const apolloClient = initApolloClient() + + // Add apolloClient to NextPageContext & NextAppContext + // This allows us to consume the apolloClient inside our + // custom `getInitialProps({ apolloClient })`. + ctx.apolloClient = apolloClient + if (inAppContext) { + ctx.ctx.apolloClient = apolloClient + } // Run wrapped getInitialProps methods let pageProps = {} @@ -61,7 +69,6 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { // Since AppComponents and PageComponents have different context types // we need to modify their props a little. - const inAppContext = Boolean(ctx.ctx) let props if (inAppContext) { props = { ...pageProps, apolloClient } From 82a54686d7fbc99cec35c085ca4c26fee9d6becf Mon Sep 17 00:00:00 2001 From: handtrix Date: Wed, 5 Feb 2020 14:41:44 +0100 Subject: [PATCH 4/5] Propertly call App.getInitialProps if used in NextAppContext --- examples/with-apollo/lib/apollo.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index 046753e2bbe2b9d..c4365e6357b40cc 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -1,4 +1,5 @@ import React from 'react' +import App from 'next/app' import Head from 'next/head' import { ApolloProvider } from '@apollo/react-hooks' import { ApolloClient } from 'apollo-client' @@ -36,6 +37,10 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { const { AppTree } = ctx const inAppContext = Boolean(ctx.ctx) + if (ctx.apolloClient) { + throw new Error('Multiple instances of withApollo found.') + } + // Initialize ApolloClient const apolloClient = initApolloClient() @@ -51,6 +56,8 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { let pageProps = {} if (PageComponent.getInitialProps) { pageProps = await PageComponent.getInitialProps(ctx) + } else if (inAppContext) { + pageProps = await App.getInitialProps(ctx) } // Only on the server: From 205014f6b103c5796c17ee3dbc9f8690a8d69c3b Mon Sep 17 00:00:00 2001 From: handtrix Date: Wed, 5 Feb 2020 14:48:52 +0100 Subject: [PATCH 5/5] Add Automatic Static Optimization warning --- examples/with-apollo/lib/apollo.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/with-apollo/lib/apollo.js b/examples/with-apollo/lib/apollo.js index c4365e6357b40cc..61721bbf2d21174 100644 --- a/examples/with-apollo/lib/apollo.js +++ b/examples/with-apollo/lib/apollo.js @@ -37,6 +37,15 @@ export const withApollo = ({ ssr = true } = {}) => PageComponent => { const { AppTree } = ctx const inAppContext = Boolean(ctx.ctx) + if (process.env.NODE_ENV === 'development') { + if (inAppContext) { + console.warn( + 'Warning: You have opted-out of Automatic Static Optimization due to `withApollo` in `pages/_app`.\n' + + 'Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n' + ) + } + } + if (ctx.apolloClient) { throw new Error('Multiple instances of withApollo found.') }