mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.61k stars 32.21k forks source link

SheetsRegistry in SSR generates empty string #9097

Closed arfianadam closed 6 years ago

arfianadam commented 6 years ago

Expected Behavior

The console.log(css) returns some CSS as string (?)

Current Behavior

The console.log(css) returns empty string "". Therefore my rendered material-ui components don't have styles.

Steps to Reproduce

I followed all steps in the official guide https://material-ui.com/guides/server-rendering/. And here is some sample of my code:

/* server.js */
...
import { SheetsRegistry, JssProvider } from 'react-jss';
import { create } from 'jss';
import preset from 'jss-preset-default';
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import createGenerateClassName from 'material-ui/styles/createGenerateClassName';
import { red } from 'material-ui/colors';
...
app.use((req, res) => {
...
  const sheetsRegistry = new SheetsRegistry();
  const theme = createMuiTheme({
    palette: {
      primary: red,
      type: 'dark',
    },
  });
  const jss = create(preset());
  jss.options.createGenerateClassName = createGenerateClassName;

  const component = (
    <Provider store={store} key="provider">
      <JssProvider registry={sheetsRegistry} jss={jss}>
        <MuiThemeProvider theme={theme} sheetsManager={new Map()}>
          <ReduxAsyncConnect {...renderProps} />
        </MuiThemeProvider>
      </JssProvider>
    </Provider>
  );

  const css = sheetsRegistry.toString();
  console.log(css); // returns empty string

  res.send(`<!doctype html>
    ${ReactDOM.renderToString(
      <Html assets={webpackIsomorphicTools.assets()} style={css} component={component} store={store} />
  )}`);
...
});

The response I got from initial render is correct, for example this AppBar component

<AppBar>
  <Toolbar>
    <IconButton color="contrast" aria-label="Menu" className={classes.menuButton}>
      <MenuIcon />
    </IconButton>
    <Typography type="title" color="inherit" className={classes.title}>
      Dashboard
    </Typography>
    <Button color="contrast">Login</Button>
  </Toolbar>
</AppBar>

Rendered to string as

<header class="c10 c16 c3 c4 c8 mui-fixed" data-reactid="4">
    <div class="c37 c38" data-reactid="5">
        <button tabindex="0" class="c48 c39 c41 c2" type="button" role="button" aria-label="Menu" data-reactid="6"><span class="c45" data-reactid="7"><svg class="c50 c46" focusable="false" viewBox="0 0 24 24" aria-hidden="true" data-reactid="8"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" data-reactid="9"></path></svg></span><span class="c51" data-reactid="10"></span>
        </button>
        <h2 class="c58 c64 c77 c1" data-reactid="11">Dashboard</h2>
        <button tabindex="0" class="c48 c82 c87" type="button" role="button" data-reactid="12"><span class="c84" data-reactid="13">Login</span><span class="c51" data-reactid="14"></span>
        </button>
    </div>
</header>

Context

Just trying to accomplish simple SSR by following official guide.

My Environment

Tech Version
Material-UI 1.0.0-beta.20
React 15.5.4
JSS 8.1.0
react-jss 7.2.0
oliviertassinari commented 6 years ago

What you are describing is the expected behavior. You are calling ReactDOM.renderToString() after sheetsRegistry.toString(). You need to reverse the calls. It's ReactDOM.renderToString() how is adding the CSS to the sheet registry.

ohmyhusky commented 6 years ago

@oliviertassinari Hi bro, I have the empty string problem too, but not like this calling renderToString after sheetsRegistry.toString, my problem is sheetsRegistry.toString can generate stylesheet string correctly in the "first call", but after that, the sheetsRegistry.toString just return empty string, I'm not sure if it's related to this one, so I just put it here first.

import { renderToString } from "react-dom/server";
import React from "react";
import { createGenerateClassName } from "material-ui/styles";
import { SheetsRegistry } from "react-jss/lib/jss";
import JssProvider from "react-jss/lib/JssProvider";
import Button from "material-ui/Button";

function render() {
  const sheetsRegistry = new SheetsRegistry();
  const generateClassName = createGenerateClassName();

  const str = renderToString(
    <JssProvider
      registry={sheetsRegistry}
      generateClassName={generateClassName}
    >
      <Button>Default</Button>
    </JssProvider>
  );

  const css = sheetsRegistry.toString();

  console.log(css.length);
}

render();
render();

The second console.log will print 0, what did I do wrong in SSR? In case that you might need to reproduce it ssr

oliviertassinari commented 6 years ago

@cyl19910101 Posting this issue on StackOverflow would be much more efficient. The stylesheets are generated only once per component type (for performance). We use a cache. You need to clear this cache between two requests. It's what the sheetsManager is for in the guide.

ohmyhusky commented 6 years ago

@oliviertassinari Thanks a lot! I was thinking that according to that guide if I don't need to override the default them then I don't need to wrap the application under MuiThemeProvider....

Fi1osof commented 6 years ago

@oliviertassinari @cyl19910101 so how can i clear cache? Lose many time for this problem..(

oliviertassinari commented 6 years ago

@Fi1osof The cache is stored in the sheetsManager. It's a simple Map object. You can provide a new one to clear the cache.

Fi1osof commented 6 years ago

@oliviertassinari It`s my mistake... mui@next do not requries MuiThemeProvider, and i have not this component in my code. yes, later i added this component and this solves my problem. Thanks for answer!

minhna commented 5 years ago

@oliviertassinari Hi bro, I have the empty string problem too, but not like this calling renderToString after sheetsRegistry.toString, my problem is sheetsRegistry.toString can generate stylesheet string correctly in the "first call", but after that, the sheetsRegistry.toString just return empty string, I'm not sure if it's related to this one, so I just put it here first.

import { renderToString } from "react-dom/server";
import React from "react";
import { createGenerateClassName } from "material-ui/styles";
import { SheetsRegistry } from "react-jss/lib/jss";
import JssProvider from "react-jss/lib/JssProvider";
import Button from "material-ui/Button";

function render() {
  const sheetsRegistry = new SheetsRegistry();
  const generateClassName = createGenerateClassName();

  const str = renderToString(
    <JssProvider
      registry={sheetsRegistry}
      generateClassName={generateClassName}
    >
      <Button>Default</Button>
    </JssProvider>
  );

  const css = sheetsRegistry.toString();

  console.log(css.length);
}

render();
render();

The second console.log will print 0, what did I do wrong in SSR? In case that you might need to reproduce it ssr

I had this issue too. Then I found that if I add sheetsManager={new Map()} to MuiThemeProvider, the problem solved.