【问题标题】:React Material-ui SSR - Warning: Prop `d` did not match. Server: "M 0 0 h 24 v 24 H 0 Z" Client: "M0 0h24v24H0z"React Material-ui SSR - 警告:道具`d`不匹配。服务器:“M 0 0 h 24 v 24 H 0 Z”客户端:“M0 0h24v24H0z”
【发布时间】:2019-08-11 19:14:28
【问题描述】:

我正在开发一个带有服务器端渲染和 Material-ui 的 React 站点。一切都很好,包括 mui JSS 的东西。

然后我从 @material-ui/icons 添加了一个 SVG 图标

现在,Edge 和 IE11 抱怨: 警告:道具d 不匹配。服务器:“M 0 0 h 24 v 24 H 0 Z”客户端:“M0 0h24v24H0z”

警告表明服务器和客户端渲染不匹配,但如果我使用 curl 获取服务器渲染,则它是正确的,并且不包括 IE/Edge 控制台中显示的空格。

所有其他浏览器(当然)都可以。

有其他人只在 MS 浏览器中遇到过 SSR 问题吗?

这是我能举出的一个例子。它基于 material-ui-master/examples/ssr 并删除了大部分内容:

server.js:

import express from "express";
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './App';

function renderFullPage(html) {
  return `
    <!doctype html>
    <html>
      <body>
        <div id="root">${html}</div>
        <script src="build/bundle.js"></script>
      </body>
    </html>
  `;
}

function handleRender(req, res) {
  // Render the component to a string.
  const html = ReactDOMServer.renderToString(
        <App />
  );

  res.send(renderFullPage(html));
}

const app = express();

app.use('/build', express.static('build'));

// This is fired every time the server-side receives a request.
app.use(handleRender);

const port = 3000;
app.listen(port);

client.js:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

class Main extends React.Component {
  render() {
    return <App />;
  }
}

ReactDOM.hydrate(
  <Main />
  , document.querySelector('#root')
);

App.js:

import React from 'react';
import { Menu } from "@material-ui/icons";

export default class App extends React.Component {
  render() {
    return (
      <Menu />
    );
  }
}

package.json:

{
  "name": "ssr",
  "version": "3.0.0",
  "private": true,
  "dependencies": {
    "@babel/core": "latest",
    "@babel/node": "latest",
    "@babel/plugin-proposal-class-properties": "latest",
    "@babel/preset-env": "^7.4.2",
    "@babel/preset-react": "latest",
    "@material-ui/core": "latest",
    "@material-ui/icons": "^3.0.2",
    "babel-loader": "next",
    "express": "latest",
    "fs": "0.0.1-security",
    "net": "^1.0.2",
    "nodemon": "latest",
    "prop-types": "latest",
    "react": "latest",
    "react-dom": "latest",
    "react-jss": "^8.1.0",
    "webpack": "latest",
    "webpack-cli": "latest"
  },
  "scripts": {
    "start-server": "SET NODE_ENV=development& nodemon --inspect ./build/server.js",
    "start": "webpack -w"
  }
}

webpack.config.js:

const path = require('path');

const browserConfig = {
  entry: './client.js',
  node: {
    fs: "empty"
  },
  mode: process.env.NODE_ENV || 'development',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'bundle.js',
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
};

const serverConfig = {
  entry: './server.js',
  target: 'node',
  node: {
    fs: "empty"
  },
  mode: process.env.NODE_ENV || 'development',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'server.js',
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
};

module.exports = [browserConfig, serverConfig]

.babel.rc:

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-proposal-class-properties"]
}

【问题讨论】:

标签: reactjs internet-explorer-11 material-ui microsoft-edge server-side-rendering


【解决方案1】:

我遇到了类似的问题。这不是 SSR 的问题,而是 JSX 中的条件渲染,其中服务器和客户端的条件不同。

在我的例子中,我根据从 localStorage 读取的条件渲染了一些内容,该条件仅在客户端上定义。服务端返回undefined,所以客户端和服务端渲染的不匹配。

我的解决方案是根据组件是否在客户端渲染来有条件地渲染。

我编写了以下自定义钩子:

import {useEffect, useState} from "react";

export const useLoaded = () => {
    const [loaded, setLoaded] = useState(false);
    useEffect(() => setLoaded(true), []);
    return loaded;
};

我就是这样使用它的:

// in the functional component's body
const loaded = useLoaded();

// in the JSX
{localCondition && loaded &&
  <MyComponent />
}

对于类组件,您将使用componentDidMount 生命周期方法和setState

【讨论】:

  • 这也是我的假设。直到我意识到它只发生在 Microsoft 浏览器中
【解决方案2】:

所以这似乎是 React 中的一个错误。我在 Material-ui 项目中记录了一个问题,导致 React 中出现了这个未解决的问题:

https://github.com/facebook/react/issues/15187

警告似乎是良性的,并且随着 Edge 迁移到 Chromium,我不再像以前那样担心了。

【讨论】:

    【解决方案3】:

    我遇到了同样的问题...不匹配服务器:...客户端:... 使用 NextJS (React) 和 Express.js 服务器(节点 server.js) .

    解决方案是仅当 process.browsertrue 时才呈现组件。

    {
      process.browser ?
      <MyComponent /> :
      null
    }
    

    【讨论】:

    • 它对我有用!但现在我得到了这个错误:Warning: Expected server HTML to contain a matching &lt;header&gt; in &lt;section&gt;.