Material-ui: Bug on NextJS hooks example when using react-apollo-hooks

Created on 10 Jan 2019  ยท  10Comments  ยท  Source: mui-org/material-ui

When using Material UI hooks with NextJS and react-apollo-hooks, received bug - TypeError: Cannot read property 'theme' of undefined at MyDocument.render (/material-ui/examples/nextjs-hooks-with-typescript/.next/server/static/development/pages/_document.js:156:38)

  • [x] This is not a v0.x issue.
  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior ๐Ÿค”

Able to use Material UI hooks and Apollo hooks together.

Current Behavior ๐Ÿ˜ฏ

undefined pageContext caused TypeError: Cannot read property 'theme' of undefined at line:
https://github.com/ivawzh/material-ui/blob/fc6ae01e66bde4252a08903719a59ef108e05e5f/examples/nextjs-hooks-with-typescript/pages/_document.tsx#L14

image

Steps to Reproduce ๐Ÿ•น

I have prepared a repo here https://github.com/ivawzh/material-ui/commit/fc6ae01e66bde4252a08903719a59ef108e05e5f

Steps:

  1. git clone [email protected]:ivawzh/material-ui.git
  2. cd material-ui/examples/nextjs-hooks-with-typescript
  3. npm install && npm run dev
  4. In web browser, open http://localhost:3000/countries2
  5. See the error in your terminal

Context ๐Ÿ”ฆ

I am trying to use Apollo hooks with Material UI hooks example.
From the repo above you will see the non-hook Apollo endpoint at http://localhost:3000/countries is working fine as expected. But when Apollo hooks is in used, app will crash because pageContext becomes undefined.

Your Environment ๐ŸŒŽ

https://github.com/ivawzh/material-ui/tree/fc6ae01e66bde4252a08903719a59ef108e05e5f/examples/nextjs-hooks-with-typescript

| Tech | Version |
|--------------|---------|
| Material-UI | v3.8.1 |
| React | 16.7.0-alpha.2 |
| Browser | Chrome |
| TypeScript | 3.2.2 |
| react-apollo | 2.3.3 |
| react-apollo-hooks | 0.2.1 |

Most helpful comment

@ivawzh We are introducing a server-side rendering API: #15030. You can apply the same pattern now:

--- a/examples/nextjs-hooks-with-typescript/pages/_app.tsx
+++ b/examples/nextjs-hooks-with-typescript/pages/_app.tsx
@@ -39,12 +39,6 @@ class MyApp extends App <Props> {
             <Head>
               <title>My page</title>
             </Head>
-            {/* Wrap every page in Styles and Theme providers */}
-            <StylesProvider
-              generateClassName={this.pageContext.generateClassName}
-              sheetsRegistry={this.pageContext.sheetsRegistry}
-              sheetsManager={this.pageContext.sheetsManager}
-            >
               {/* ThemeProvider makes the theme available down the React
                   tree thanks to React context. */}
               <ThemeProvider theme={this.pageContext.theme}>
@@ -54,7 +48,6 @@ class MyApp extends App <Props> {
                     to render collected styles on server side. */}
                 <Component pageContext={this.pageContext} {...pageProps} />
               </ThemeProvider>
-            </StylesProvider>
           </ApolloHooksProvider>
         </ApolloProvider>
       </Container>
diff --git a/examples/nextjs-hooks-with-typescript/pages/_document.tsx b/examples/nextjs-hooks-with-typescript/pages/_document.tsx
index bb5ddad8c..a37a6f1bd 100644
--- a/examples/nextjs-hooks-with-typescript/pages/_document.tsx
+++ b/examples/nextjs-hooks-with-typescript/pages/_document.tsx
@@ -2,7 +2,8 @@ import { MuiThemeProviderProps } from '@material-ui/core/styles/MuiThemeProvider
 import Document, { AnyPageProps, Head, Main, NextScript, PageProps } from 'next/document';
 import React, { ComponentType } from 'react';
 import flush from 'styled-jsx/server';
-import { PageContext } from '../src/getPageContext';
+import getPageContext, { PageContext } from '../src/getPageContext';
+import { StylesProvider, ThemeProvider } from '@material-ui/styles';

 class MyDocument extends Document<{
   pageContext: MuiThemeProviderProps;
@@ -43,7 +44,7 @@ interface PagePropsWithPageContext extends AnyPageProps {
   pageContext: PageContext;
 }

-MyDocument.getInitialProps = ctx => {
+MyDocument.getInitialProps = async ctx => {
   // Resolution order
   //
   // On the server:
@@ -66,16 +67,17 @@ MyDocument.getInitialProps = ctx => {
   // 3. app.render
   // 4. page.render

-  // Render app and page and get the context of the page with collected side effects.
-  let pageContext: PageContext | undefined;
-  const page = ctx.renderPage((Component: ComponentType<PagePropsWithPageContext>) => {
-    const WrappedComponent: ComponentType<{ pageContext: PageContext } & PageProps> = props => {
-      pageContext = props.pageContext;
-      return <Component {...props} />;
-    };
+  const originalRenderPage = ctx.renderPage;
+  const pageContext = getPageContext();

-    return WrappedComponent;
-  });
+  ctx.renderPage = () =>
+    originalRenderPage({
+      enhanceApp: App => props => (<StylesProvider
+              sheetsRegistry={pageContext.sheetsRegistry}
+              sheetsManager={pageContext.sheetsManager}><App {...props} /></StylesProvider>),
+    });
+
+  const initialProps = await Document.getInitialProps(ctx);

   let css;
   // It might be undefined, e.g. after an error.
@@ -84,7 +86,7 @@ MyDocument.getInitialProps = ctx => {
   }

   return {
-    ...page,
+    ...initialProps,
     pageContext,
     // Styles fragment is rendered after the app and page rendering finish.
     styles: (

It fixes the problem on my end.

All 10 comments

Could you humor me and do a yarn list hoist-non-react-statics where the example is installed and post the resutls?

Sure.

โžœ  nextjs-hooks-with-typescript git:(nextjs-apollo-hooks) yarn list hoist-non-react-statics
yarn list v1.9.4
warning Filtering by arguments is deprecated. Please use the pattern option instead.
โ”œโ”€ @material-ui/[email protected]
โ”‚  โ””โ”€ [email protected]
โ”œโ”€ [email protected]
โ””โ”€ [email protected]
   โ””โ”€ [email protected]
โœจ  Done in 0.41s.

Update

Also did yarn add [email protected] just for fun.

After that, bug still persists and yarn list looks like this

โžœ  nextjs-hooks-with-typescript git:(nextjs-apollo-hooks) โœ— yarn list hoist-non-react-statics
yarn list v1.9.4
warning Filtering by arguments is deprecated. Please use the pattern option instead.
โ”œโ”€ [email protected]
โ”œโ”€ [email protected]
โ”‚  โ””โ”€ [email protected]
โ””โ”€ [email protected]
   โ””โ”€ [email protected]
โœจ  Done in 0.41s.

@ivawzh This is an integration issue with Apollo. I'm not sure it's the right place to open an issue. It would suggest we move the issue to stackoverflow.

@oliviertassinari right. will do.

I think the examples are here to provide seamless integration with the other popular libs like NextJs or Apollo. And the error trace is pointing to the static _document file from the code written by Material UI example. Which suggests the example is less extensible than I hope it is. So that I opened a ticket here.

Anyway, just created an stackoverflow question here https://stackoverflow.com/questions/54155865/bug-on-integrating-material-ui-hooks-and-apollo-hooks

Appreciated your helps ๐Ÿ˜ƒ

@ivawzh I'm personally using Next.js + Apollo + Material-UI at https://www.onepixel.com/. Now, there is one important difference. We are not using the _app.js capability as in the example on the Material-UI repository, at least not yet. We are also not using next-with-apollo, we are using something custom (Apollo is a performance killer on the server, the cache is slow, the DOM tree traversal is extremely slow, the query formatting is slow, to mitigate the issue, we conditionally disable getDataFromTree in the pages). I will give a second look at the problem.

@oliviertassinari Heaps of thanks! That is some super useful information. I will also reconsider using Apollo on SSR if perf is so bad.

Is there any chance that you have some public available example of the setup you mentioned or maybe blog about the perf/benchmark and mitigation?

@ivawzh If you move the server-side queries to get getIntialProps, it's not great but doable. Google Crawler stats are around 300ms of latency on our website (we index millions of pages). Ideally, we would like to see 100ms, it's important for websites relying on SEO with a lot a lot a lot of pages. The DX is awesome, I think that it worth the tradeoff. I have nothing public if not the examples we have here.

@ivawzh We are introducing a server-side rendering API: #15030. You can apply the same pattern now:

--- a/examples/nextjs-hooks-with-typescript/pages/_app.tsx
+++ b/examples/nextjs-hooks-with-typescript/pages/_app.tsx
@@ -39,12 +39,6 @@ class MyApp extends App <Props> {
             <Head>
               <title>My page</title>
             </Head>
-            {/* Wrap every page in Styles and Theme providers */}
-            <StylesProvider
-              generateClassName={this.pageContext.generateClassName}
-              sheetsRegistry={this.pageContext.sheetsRegistry}
-              sheetsManager={this.pageContext.sheetsManager}
-            >
               {/* ThemeProvider makes the theme available down the React
                   tree thanks to React context. */}
               <ThemeProvider theme={this.pageContext.theme}>
@@ -54,7 +48,6 @@ class MyApp extends App <Props> {
                     to render collected styles on server side. */}
                 <Component pageContext={this.pageContext} {...pageProps} />
               </ThemeProvider>
-            </StylesProvider>
           </ApolloHooksProvider>
         </ApolloProvider>
       </Container>
diff --git a/examples/nextjs-hooks-with-typescript/pages/_document.tsx b/examples/nextjs-hooks-with-typescript/pages/_document.tsx
index bb5ddad8c..a37a6f1bd 100644
--- a/examples/nextjs-hooks-with-typescript/pages/_document.tsx
+++ b/examples/nextjs-hooks-with-typescript/pages/_document.tsx
@@ -2,7 +2,8 @@ import { MuiThemeProviderProps } from '@material-ui/core/styles/MuiThemeProvider
 import Document, { AnyPageProps, Head, Main, NextScript, PageProps } from 'next/document';
 import React, { ComponentType } from 'react';
 import flush from 'styled-jsx/server';
-import { PageContext } from '../src/getPageContext';
+import getPageContext, { PageContext } from '../src/getPageContext';
+import { StylesProvider, ThemeProvider } from '@material-ui/styles';

 class MyDocument extends Document<{
   pageContext: MuiThemeProviderProps;
@@ -43,7 +44,7 @@ interface PagePropsWithPageContext extends AnyPageProps {
   pageContext: PageContext;
 }

-MyDocument.getInitialProps = ctx => {
+MyDocument.getInitialProps = async ctx => {
   // Resolution order
   //
   // On the server:
@@ -66,16 +67,17 @@ MyDocument.getInitialProps = ctx => {
   // 3. app.render
   // 4. page.render

-  // Render app and page and get the context of the page with collected side effects.
-  let pageContext: PageContext | undefined;
-  const page = ctx.renderPage((Component: ComponentType<PagePropsWithPageContext>) => {
-    const WrappedComponent: ComponentType<{ pageContext: PageContext } & PageProps> = props => {
-      pageContext = props.pageContext;
-      return <Component {...props} />;
-    };
+  const originalRenderPage = ctx.renderPage;
+  const pageContext = getPageContext();

-    return WrappedComponent;
-  });
+  ctx.renderPage = () =>
+    originalRenderPage({
+      enhanceApp: App => props => (<StylesProvider
+              sheetsRegistry={pageContext.sheetsRegistry}
+              sheetsManager={pageContext.sheetsManager}><App {...props} /></StylesProvider>),
+    });
+
+  const initialProps = await Document.getInitialProps(ctx);

   let css;
   // It might be undefined, e.g. after an error.
@@ -84,7 +86,7 @@ MyDocument.getInitialProps = ctx => {
   }

   return {
-    ...page,
+    ...initialProps,
     pageContext,
     // Styles fragment is rendered after the app and page rendering finish.
     styles: (

It fixes the problem on my end.

@oliviertassinari Compared to the now available nextjs-hooks-with-typescript example from december, beside the ones you posted are there any other changes in it? If yes, where can I find the updated example?
The next branch never had this example, and the base branch version never got this update.

@bozada You probably need this example: /examples/nextjs-next-with-typescript. It's TypeScript + Next.js + Material-UI v4 (with the new SSR API).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  ยท  3Comments

mb-copart picture mb-copart  ยท  3Comments

reflog picture reflog  ยท  3Comments

pola88 picture pola88  ยท  3Comments

TimoRuetten picture TimoRuetten  ยท  3Comments