Next.js: Next runs components multiple times on the serverside.

Created on 5 Sep 2018  路  12Comments  路  Source: vercel/next.js

Bug report

Describe the bug

Seems like Next SSR creates runs imported modules multiple times, or instantiates components multiple times more than necessary, because I get the following error on the clientside when using JSS (react-jss):

Warning: Prop `className` did not match. Server: "Footer-lowLogo-0-1-63" Client: "Footer-lowLogo-0-1-3"

The way JSS generates the 63 in 0-1-63 is that it increments a counter. So obviously on the server side the counter is reaching 63, while on the client side it is reaching the expected count of 3.

My component modules look like this:

import {Component} from 'react'
import withStyle, {jss} from 'react-jss'

interface IProps {
  classes: any
}

export default
withStyle(style())(class Footer extends Component<IProps> {
  render() {
    const {classes} = this.props
    return (
      <div className={classes.footer}>
        <div className={classes.logoAndCopyright}></div>
      </div>
    )
  }
})

function style() { return {
  footer: {
    backgroundColor: '#f1f1f1',

    '& $logoAndCopyright': {
      paddingTop: 30,
      paddingBottom: 25,
    },
  },

  logoAndCopyright: {},
}}

Because withStyles is used during module evaluation, this hints to me that the module may be evaluated multiple times. Either that, or the component is being created multiple times needlessly.

Either way, Next.js is running something multiple times on the back end unexpectedly. This is the only way it could possibly increment the counter so high.

To Reproduce

  1. Try using react-jss on some of your components, then you should have the problem.

Expected behavior

It should run as many times as on the client.

Screenshots

n/a

System information

  • Version of Next.js: 6.1.1

Most helpful comment

material-ui provides an example with Next.js: https://github.com/mui-org/material-ui/tree/master/examples/nextjs

Also, material-ui.com is built on Next.js.

All 12 comments

Ouch, look what problem this causes with other libraries: https://github.com/mui-org/material-ui/issues/12203

Oof, here's a suggested way to make it work, another ouch:

https://material-ui.com/guides/server-rendering/#css-works-on-only-on-first-load

Darn. I really don't wanna do all that.

Either way, Next.js is running something multiple times on the back end unexpectedly. This is the only way it could possibly increment the counter so high.

This is not the case, Next only renders 1 time per request. There are a few libraries (that you have to manually implement) that do tree traversal which calls render, for example, Apollo does this.

material-ui provides an example with Next.js: https://github.com/mui-org/material-ui/tree/master/examples/nextjs

Also, material-ui.com is built on Next.js.

It is solved like this, by making sure there is one react-jss SheetsRegistry per Document instance.

This seems to indicate that Document is created more than once (with the same JSS SheetsRegistry from the react-jss module if we don't do the above trick) on the server.

So I solved this in my app by doing the same trick.

Otherwise, what else would make the ID counter increment so many times? When I do a hard refresh, is it making multiple requests? Why are there multiple instances of Document?

EDIT: Maybe loading non-existing resources causes the 404 page to trigger Document, all on a single page refresh?

@trusktr You have a Next.js example on Material-UI. I'm using Next.js in production, no issue.

@timneutkens

I stuck some debuggers in my _document.tsx, like this:

import Document, { Head, Main, NextScript } from 'next/document'
import { JssProvider, SheetsRegistry } from 'react-jss';

type HOC = (App: any) => (props: any) => JSX.Element

debugger

export default
class MyDocument extends Document {

  constructor(props: any) {
    super(props)
    debugger
  }

  static getInitialProps({ renderPage }: { renderPage(hoc: HOC): any }) {
    debugger
    const sheets = new SheetsRegistry();

    const decoratePage: HOC = App => props => (
      <JssProvider registry={sheets}>
        <App {...props} />
      </JssProvider>
    );

    const renderedPage = renderPage(decoratePage);

    const styles = (
      <style type="text/css" data-meta="jss-ssr" dangerouslySetInnerHTML={{ __html: sheets.toString() }} />
    );

    return { ...renderedPage, styles };
  }

  // ...
}

and I found that after starting next then visiting my page, it ran through all three debuggers twice (including the debugger in the top-level module).

Then, when I hit refresh in the browser, it runs through both debugger inside the class twice, but not the top-level module.

I think what causes the second run-through is that I'm missing manifest.json which 404s:

screen shot 2018-09-06 at 1 02 43 pm

Why does it run the top-level module twice?

I'm not sure if this is what's causing my react-jss SheetsRegistry class names to be out of sync on the server though.

The react-jss-next-demo works fine with the same static getInitialProps method. In my app I'm using styles the exact same way on my components (I think).

I guess the favicon request? 馃

I don't think it should. Generally I've noticed my app starting, then restarting, on first run. Not a biggie though, after that everything continues as normal (hot modules on change).

I guess the favicon request? 馃

Yh. That's a pain. Any way to stop that?

material-ui provides an example with Next.js: https://github.com/mui-org/material-ui/tree/master/examples/nextjs

Also, material-ui.com is built on Next.js.

This did it for me.

This is not the case, Next only renders 1 time per request. There are a few libraries (that you have to manually implement) that do tree traversal which calls render, for example, Apollo does this.

Today I learned. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

swrdfish picture swrdfish  路  3Comments

formula349 picture formula349  路  3Comments

sospedra picture sospedra  路  3Comments

irrigator picture irrigator  路  3Comments

DvirSh picture DvirSh  路  3Comments