简体   繁体   中英

Material UI:Flickering on the initial load of the page(Razzle)

I have integrated material UI in my Server side rendering app and have followed the code given here . Material UI css is been added to the html on the server side itself.

However, on the initial load, material ui styles are not applied which is causing it to flicker.

I tried using <cssbaseline> along with the below code but did not see any difference.

Here is my code: server.js

import App from '../common/containers/App';
import { Provider } from 'react-redux';
import React from 'react';
import configureStore from '../common/store/configureStore';
import express from 'express';
import qs from 'qs';
import { renderToString } from 'react-dom/server';
import serialize from 'serialize-javascript';
import { StaticRouter, matchPath } from 'react-router-dom';
import {theme} from '../theme';
import { ServerStyleSheets, ThemeProvider } from '@material-ui/styles';
import routes from './routes'
import "../common/assets/SCSS/app.scss";

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
  .disable('x-powered-by')
  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))
  .get('/*', (req, res) => {
    const activeRoute = routes.find((route) => matchPath(req.url, route)) || {}
    const promise = activeRoute.fetchInitialData
      ? activeRoute.fetchInitialData(req.path)
      : Promise.resolve()
    promise.then((apiResult) => {
      const sheets = new ServerStyleSheets();
      const counter =  apiResult || 0;
      const preloadedState = { counter };
      const store = configureStore(preloadedState);
      const context = {};
      const markup = renderToString(
         sheets.collect(
           <Provider store={store}>
            <ThemeProvider theme={theme}>
              <StaticRouter location={req.url} context={context}>
                <App />
              </StaticRouter>
            </ThemeProvider>
          </Provider>
      ));
   const css = sheets.toString();
      const finalState = store.getState();
      const html = `<!doctype html>
          <html lang="">
          <head>
              ${assets.client.css
                ? `<link rel="stylesheet" href="${assets.client.css}">`
                : ''}
                ${css ? `<style id='jss-ssr'>${css}</style>` : ''}
                ${process.env.NODE_ENV === 'production'
                  ? `<script src="${assets.client.js}" defer></script>`
                  : `<script src="${assets.client.js}" defer crossorigin></script>`}
          </head>
          <body>
              <div id="root">${markup}</div>
              <script>
                window.__PRELOADED_STATE__ = ${serialize(finalState)}
              </script>
          </body>
      </html>`
 res.send(html);
    });
  });

export default server;

Client.js

import React,{useEffect} from 'react';
import { hydrate } from 'react-dom';
import {theme} from '../theme';
import { ThemeProvider } from '@material-ui/styles';
import { Provider } from 'react-redux';
import configureStore from '../common/store/configureStore';
import App from '../common/containers/App';
import { BrowserRouter } from "react-router-dom";
import "../common/assets/SCSS/app.scss";
import * as serviceWorker from '../serviceWorker';
import CssBaseline from '@material-ui/core/CssBaseline';

const store = configureStore(window.__PRELOADED_STATE__);
const Client = () => {
  useEffect(() => {
   
    const jssStyles = document.querySelector('#jss-ssr');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return(
    <Provider store={store}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </ThemeProvider>
    </Provider>
  );

}


hydrate(<Client />,document.getElementById('root'));

This is caused by your client side code removing the server generated styling before the client styling has been generated.

The hydrate function provided by react-dom has a 3rd parameter which is a callback function that is called after hydration of the app is complete.

This callback function should be used to ensure that the app has completed hydration and has generated the client styling.

import React,{useEffect} from 'react';
import { hydrate } from 'react-dom';
import {theme} from '../theme';
import { ThemeProvider } from '@material-ui/styles';
import { Provider } from 'react-redux';
import configureStore from '../common/store/configureStore';
import App from '../common/containers/App';
import { BrowserRouter } from "react-router-dom";
import "../common/assets/SCSS/app.scss";
import * as serviceWorker from '../serviceWorker';
import CssBaseline from '@material-ui/core/CssBaseline';

const store = configureStore(window.__PRELOADED_STATE__);

const Client = () => {
  return(
    <Provider store={store}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </ThemeProvider>
    </Provider>
  );

}


hydrate(
    <Client />,
    document.getElementById('root'),
    () => {
        const jssStyles = document.querySelector('#jss-ssr');

        if (jssStyles) {
            jssStyles.parentElement.removeChild(jssStyles);
        }
    }
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM