From ca33d9773581a8b0ff37c92260b48c9f72b42de9 Mon Sep 17 00:00:00 2001
From: NikitaVlaznev <nikita.dto@gmail.com>
Date: Wed, 27 Jun 2018 23:30:40 +0300
Subject: [PATCH] [docs] Use _app.js instead of wrapping every page by
 withRoot() (#11989)

* [examples] Avoid wrapping every page by withRoot

* ready to be merged
---
 examples/nextjs/pages/_app.js      | 51 +++++++++++++++++++++++++++
 examples/nextjs/pages/_document.js | 47 ++++++++++++++-----------
 examples/nextjs/pages/about.js     | 44 ++++++++++++++++++++++++
 examples/nextjs/pages/index.js     | 11 ++++--
 examples/nextjs/src/withRoot.js    | 55 ------------------------------
 5 files changed, 132 insertions(+), 76 deletions(-)
 create mode 100644 examples/nextjs/pages/_app.js
 create mode 100644 examples/nextjs/pages/about.js
 delete mode 100644 examples/nextjs/src/withRoot.js

diff --git a/examples/nextjs/pages/_app.js b/examples/nextjs/pages/_app.js
new file mode 100644
index 00000000000000..2ed955dde6d8fc
--- /dev/null
+++ b/examples/nextjs/pages/_app.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import App, { Container } from 'next/app';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import CssBaseline from '@material-ui/core/CssBaseline';
+import JssProvider from 'react-jss/lib/JssProvider';
+import getPageContext from '../src/getPageContext';
+
+class MyApp extends App {
+  constructor(props) {
+    super(props);
+    this.pageContext = getPageContext();
+  }
+
+  pageContext = null;
+
+  componentDidMount() {
+    // Remove the server-side injected CSS.
+    const jssStyles = document.querySelector('#jss-server-side');
+    if (jssStyles && jssStyles.parentNode) {
+      jssStyles.parentNode.removeChild(jssStyles);
+    }
+  }
+
+  render() {
+    const { Component, pageProps } = this.props;
+    return (
+      <Container>
+        {/* Wrap every page in Jss and Theme providers */}
+        <JssProvider
+          registry={this.pageContext.sheetsRegistry}
+          generateClassName={this.pageContext.generateClassName}
+        >
+          {/* MuiThemeProvider makes the theme available down the React
+              tree thanks to React context. */}
+          <MuiThemeProvider
+            theme={this.pageContext.theme}
+            sheetsManager={this.pageContext.sheetsManager}
+          >
+            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
+            <CssBaseline />
+            {/* Pass pageContext to the _document though the renderPage enhancer
+                to render collected styles on server side. */}
+            <Component pageContext={this.pageContext} {...pageProps} />
+          </MuiThemeProvider>
+        </JssProvider>
+      </Container>
+    );
+  }
+}
+
+export default MyApp;
diff --git a/examples/nextjs/pages/_document.js b/examples/nextjs/pages/_document.js
index 26203616889abc..a0c3cbb9af20b4 100644
--- a/examples/nextjs/pages/_document.js
+++ b/examples/nextjs/pages/_document.js
@@ -1,8 +1,7 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import Document, { Head, Main, NextScript } from 'next/document';
-import JssProvider from 'react-jss/lib/JssProvider';
 import flush from 'styled-jsx/server';
-import getPageContext from '../src/getPageContext';
 
 class MyDocument extends Document {
   render() {
@@ -41,34 +40,44 @@ MyDocument.getInitialProps = ctx => {
   // Resolution order
   //
   // On the server:
-  // 1. page.getInitialProps
-  // 2. document.getInitialProps
-  // 3. page.render
-  // 4. document.render
+  // 1. app.getInitialProps
+  // 2. page.getInitialProps
+  // 3. document.getInitialProps
+  // 4. app.render
+  // 5. page.render
+  // 6. document.render
   //
   // On the server with error:
-  // 2. document.getInitialProps
+  // 1. document.getInitialProps
+  // 2. app.render
   // 3. page.render
   // 4. document.render
   //
   // On the client
-  // 1. page.getInitialProps
-  // 3. page.render
+  // 1. app.getInitialProps
+  // 2. page.getInitialProps
+  // 3. app.render
+  // 4. page.render
+
+  // Render app and page and get the context of the page with collected side effects.
+  let pageContext;
+  const page = ctx.renderPage(Component => {
+    const WrappedComponent = props => {
+      pageContext = props.pageContext;
+      return <Component {...props} />;
+    };
+
+    WrappedComponent.propTypes = {
+      pageContext: PropTypes.object.isRequired,
+    };
 
-  // Get the context of the page to collected side effects.
-  const pageContext = getPageContext();
-  const page = ctx.renderPage(Component => props => (
-    <JssProvider
-      registry={pageContext.sheetsRegistry}
-      generateClassName={pageContext.generateClassName}
-    >
-      <Component pageContext={pageContext} {...props} />
-    </JssProvider>
-  ));
+    return WrappedComponent;
+  });
 
   return {
     ...page,
     pageContext,
+    // Styles fragment is rendered after the app and page rendering finish.
     styles: (
       <React.Fragment>
         <style
diff --git a/examples/nextjs/pages/about.js b/examples/nextjs/pages/about.js
new file mode 100644
index 00000000000000..611c4113904d51
--- /dev/null
+++ b/examples/nextjs/pages/about.js
@@ -0,0 +1,44 @@
+/* eslint-disable jsx-a11y/anchor-is-valid */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import Button from '@material-ui/core/Button';
+import Typography from '@material-ui/core/Typography';
+import { withStyles } from '@material-ui/core/styles';
+import Link from 'next/link';
+
+const styles = theme => ({
+  root: {
+    textAlign: 'center',
+    paddingTop: theme.spacing.unit * 20,
+  },
+});
+
+function About(props) {
+  const { classes } = props;
+
+  return (
+    <div className={classes.root}>
+      <Typography variant="display1" gutterBottom>
+        Material-UI
+      </Typography>
+      <Typography variant="subheading" gutterBottom>
+        about page
+      </Typography>
+      <Typography gutterBottom>
+        <Link href="/">
+          <a>Go to the main page</a>
+        </Link>
+      </Typography>
+      <Button variant="contained" color="primary">
+        Do nothing button
+      </Button>
+    </div>
+  );
+}
+
+About.propTypes = {
+  classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(About);
diff --git a/examples/nextjs/pages/index.js b/examples/nextjs/pages/index.js
index 34a48257b7de18..0ef5a8a6648d76 100644
--- a/examples/nextjs/pages/index.js
+++ b/examples/nextjs/pages/index.js
@@ -1,3 +1,5 @@
+/* eslint-disable jsx-a11y/anchor-is-valid */
+
 import React from 'react';
 import PropTypes from 'prop-types';
 import Button from '@material-ui/core/Button';
@@ -8,7 +10,7 @@ import DialogContentText from '@material-ui/core/DialogContentText';
 import DialogActions from '@material-ui/core/DialogActions';
 import Typography from '@material-ui/core/Typography';
 import { withStyles } from '@material-ui/core/styles';
-import withRoot from '../src/withRoot';
+import Link from 'next/link';
 
 const styles = theme => ({
   root: {
@@ -57,6 +59,11 @@ class Index extends React.Component {
         <Typography variant="subheading" gutterBottom>
           example project
         </Typography>
+        <Typography gutterBottom>
+          <Link href="/about">
+            <a>Go to the about page</a>
+          </Link>
+        </Typography>
         <Button variant="contained" color="secondary" onClick={this.handleClick}>
           Super Secret Password
         </Button>
@@ -69,4 +76,4 @@ Index.propTypes = {
   classes: PropTypes.object.isRequired,
 };
 
-export default withRoot(withStyles(styles)(Index));
+export default withStyles(styles)(Index);
diff --git a/examples/nextjs/src/withRoot.js b/examples/nextjs/src/withRoot.js
deleted file mode 100644
index 579b728752e038..00000000000000
--- a/examples/nextjs/src/withRoot.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { MuiThemeProvider } from '@material-ui/core/styles';
-import CssBaseline from '@material-ui/core/CssBaseline';
-import getPageContext from './getPageContext';
-
-function withRoot(Component) {
-  class WithRoot extends React.Component {
-    pageContext = null;
-
-    constructor(props) {
-      super(props);
-
-      this.pageContext = this.props.pageContext || getPageContext();
-    }
-
-    componentDidMount() {
-      // Remove the server-side injected CSS.
-      const jssStyles = document.querySelector('#jss-server-side');
-      if (jssStyles && jssStyles.parentNode) {
-        jssStyles.parentNode.removeChild(jssStyles);
-      }
-    }
-
-    render() {
-      // MuiThemeProvider makes the theme available down the React tree thanks to React context.
-      return (
-        <MuiThemeProvider
-          theme={this.pageContext.theme}
-          sheetsManager={this.pageContext.sheetsManager}
-        >
-          {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
-          <CssBaseline />
-          <Component {...this.props} />
-        </MuiThemeProvider>
-      );
-    }
-  }
-
-  WithRoot.propTypes = {
-    pageContext: PropTypes.object,
-  };
-
-  WithRoot.getInitialProps = ctx => {
-    if (Component.getInitialProps) {
-      return Component.getInitialProps(ctx);
-    }
-
-    return {};
-  };
-
-  return WithRoot;
-}
-
-export default withRoot;