Next.js: Add material-ui example

Created on 25 Jan 2017  路  25Comments  路  Source: vercel/next.js

Example can be found here:
https://github.com/zeit/next.js/issues/873

good first issue

Most helpful comment

The userAgent needs to be passed in to the MUI Component. @sstubbs which version of MUI are you on? I'm running 2-beta.

As for the tap plugin, I just did the following. Bit stinky but not had any problems with it...

try {
    injectTapEventPlugin();
} catch(e) {}

Actually found that on one of the issues on the repo regarding it.

All 25 comments

The code with the page decorator works great. I'm still getting this error

React attempted to reuse markup in a container but the checksum was invalid

Maybe it's just something I'm doing wrong but it would be great if there is something anyone knows about this. I also can't find the cleanest way to add injectTapEventPlugin() where it just gets run once.

The userAgent needs to be passed in to the MUI Component. @sstubbs which version of MUI are you on? I'm running 2-beta.

As for the tap plugin, I just did the following. Bit stinky but not had any problems with it...

try {
    injectTapEventPlugin();
} catch(e) {}

Actually found that on one of the issues on the repo regarding it.

Here's a basic setup with setting the user agent and firing the injectTapEventPlugin()

import React, { Component } from 'react'
import { MuiThemeProvider, getMuiTheme } from 'material-ui/styles'
import injectTapEventPlugin from 'react-tap-event-plugin'

//Need this for material ui click events
try {
    injectTapEventPlugin()
}
catch(e) {
    //Don't do anything
}

export default class extends Component {

    static async getInitialProps ({ req }) {
        const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
                return { userAgent }
    }

    render() {

        return(
            <MuiThemeProvider muiTheme={getMuiTheme({this.props.userAgent}}>
                <YourMaterialComponent/>
            </MuiThemeProvider>
        )
    }

}

Edited answer based on feedback from @Ehesp . Thanks for pointing out the issues :)

@russelh15 I don't understand why your doing that? The server is just rendering the React before the client sees it, so it works fine rendering on the server.

@Ehesp You're right, it seems like it must be something specific to some components that I'm rendering that's causing the client render to be different from the server render.

I stripped out some code and did a simple test. It does seem to work as expected when I render a simple child component without using a isLoading state.

I'll edit my reply per your feedback.

I am running next.js 2.0 beta and MUI 0.16.7 and using the template in the example. And loading the tap plugin as suggested works great. I am still getting this error though. I am using the component given in the example which does pass the userAgent via the props and gets it as below. setting userAgent to false seems a bit strange to me but if this is really the answer I will go ahead and try it.

    static async getInitialProps(ctx) {
        const { req } = ctx;
        const isServer = !!req;
        const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;

        // Second param here is initial redux state on the server
        const store = initStore(reducers, {}, isServer);

        let pageProps = {};

        if (ComposedComponent.getInitialProps) {
            pageProps = await ComposedComponent.getInitialProps(ctx);
        }

        return {
            ...pageProps,
            initialState: store.getState(),
            isServer,
            userAgent,
        };
    }

@sstubbs that's correct - don't set the userAgent to false.

muiTheme={getMuiTheme({ userAgent: this.props.userAgent })}

Ok thanks. I'm still getting this mismatch between server and client though. Is anyone else getting this?

I'm working on an app that uses material-ui and everything works really well. Here it is if it would help at all: https://github.com/knipferrc/plate. I'm not very far on this, but I don't get any errors with difference in client and server or with the tap event.

Yours works well in chrome but I get the same issue in safari. Very strange. Still better than mine though where it happens in both browsers.

modified nextjs-apollo example (https://github.com/zeit/next.js/tree/master/examples/with-apollo) according to #873 and this issue, works fine
here is the gist with changes https://gist.github.com/alexedev/08acfd293ae88d5e353a6a6d2efcf6c9

PS i'm not sure if I placed injectTapEventPlugin() properly though

Here is how I make it work
App.js

import injectTapEventPlugin from 'react-tap-event-plugin'
if (typeof window !== 'undefined') {
  injectTapEventPlugin();
}

here is configs of material-ui:

const muiDetaulTheme = {
  userAgent: false,
};
<MuiThemeProvider muiTheme={muiTheme}>
...
</MuiThemeProvider>

I'm getting the same error in my web app as well:

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) :auto;mui-prepared:;" data-reactid="19">
 (server) :auto;mui-prepared:;-webkit-transition:h

I've tried setting the user agent correctly as well as using the try / catch statement, but I still get the above error. What was the agreed upon fix?

@justswim Can you post your code?

Hello. I have the same problem as @justswim
Here is my code https://gist.github.com/bogdibota/b97fd372c8a8b734ef42386bc33a9e94

I am also having this problem. Here is the barebones code that produces the error.

// pages/index.js
import { Card } from 'material-ui/Card'
import { MuiThemeProvider } from 'material-ui'
import getMuiTheme from 'material-ui/styles/getMuiTheme'

const Index = ({ userAgent }) => (
  <MuiThemeProvider theme={getMuiTheme({userAgent})}>
    <Card />
  </MuiThemeProvider>
)

Index.getInitialProps = ({req}) => ({
  userAgent: req ? req.headers['user-agent'] : navigator.userAgent
})

export default Index

And the error itself:

commons.js:102 Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) dex:1;mui-prepared:;" data-reactid="2"><
 (server) dex:1;mui-prepared:;-webkit-transition:a

I also tried setting the userAgent to false and used console.log to make sure the userAgent is the same between server and client.

Edit: Nevermind, MuiThemeProvider's prop is called muiTheme, not theme. This fixed it:

<MuiThemeProvider muiTheme={getMuiTheme({userAgent})}>

While this solution works... is there any way to avoid having to cut-paste the userAgent boilerplate into every single page I create? It seems that because of the way getInitialProps works, I'm prevented from using composition to solve it, since getInitialProps will only run on the outer component and not the wrapped component. Inheritance isn't possible mainly due to React itself, and I'm not quite sure both static methods (parent class and child class) would even get called.

If I'm going to need that userAgent value on every page to avoid the style checksum issue, it'd be nice to put that code in one place. I'm not seeing any way to accomplish that though.

@ryanfields you can use a higher order component to avoid having that code in multiple places.

Something like this.

`
./hoc/layout.js

const Layout = (ComposedComponent) => {

return class extends Component {

    static async getInitialProps(ctx) {

        const { req } = ctx

        const userAgent = req ? req.headers['user-agent'] : navigator.userAgent;

        let pageProps = {}

        if(ComposedComponent.getInitialProps) {
            pageProps = await ComposedComponent.getInitialProps(ctx)
        }

        return {
            ...pageProps,
            userAgent
        }

    }

    render() {

        return <ComposedComponent {...this.props} />
    }

}

}

export Layout

./pages/myPage.js

import Layout from './hoc/layout'

class MyPage extends Component {

static async getInitialProps(ctx) {

    //Do your standard stuff here

    return {

    }
}

render() {
    return <div>Hello World</div>
}

}

export default Layout(MyPage)
`

@russelh15 That's what I meant by composition. But when I tried that, Layout worked as expected, but if I also had a getInitialProps on MyPage, it was never called.

It should be called. Can you post your code?

@russelh15 I wrote something quick in isolation to test and post here, but it worked. I must be doing something weird elsewhere in my main project. I'm going to assume I've screwed something up elsewhere since this proves it should be working.

EDIT: Figured it out. I was also using muiThemeable(), which of course knows nothing about getInitialProps, so my own higher order component was never in direct contact with my inner getInitialProps component. Not using muiThemeable() solves the problem, but now I have to figure out how I'm going to access the Material-UI theme data on pages that need to get at it.

Looks like Material-UI has a new version coming out soon.

https://material-ui-1dab0.firebaseapp.com/

If someone can update the example to include usage and SSR for the new version that would be awesome.

Did a quick search found out someone already reported it:
https://github.com/zeit/next.js/issues/2800


I actually get an error when running the example, looks like either the example isn't up to date anymore, wrong, or I did something wrong, but this error came from within the example, so I guess it is the former?:

(0 , _styles.createStyleSheet) is not a function
TypeError: (0 , _styles.createStyleSheet) is not a function
    at Object.<anonymous> (...project/.next/dist/hocs/withRoot.js:48:47)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (...project/.next/dist/pages/index.js:42:17)
    at Module._compile (module.js:569:30)

This is fixed now. It was an error with a commit on v1 that removed createStyleSheet.

Was this page helpful?
0 / 5 - 0 ratings