Node.js 反应+;物料界面-警告:道具类别名称不匹配

Node.js 反应+;物料界面-警告:道具类别名称不匹配,node.js,reactjs,material-ui,nextjs,Node.js,Reactjs,Material Ui,Nextjs,我很难理解Material UI组件中样式的客户端呈现和服务器端呈现之间的差异,因为类名的分配不同 在第一次加载页面时,类名被正确分配,但在刷新页面后,类名不再匹配,因此组件将丢失其样式。这是我在控制台上收到的错误消息: 警告:道具className不匹配。 服务器:“MuiFormControl-root-3 MuiFormControl-marginNormal-4 搜索栏-textField-31“ 客户:“MuiFormControl-root-3 MuiFormControl-marg

我很难理解Material UI组件中样式的客户端呈现和服务器端呈现之间的差异,因为类名的分配不同

在第一次加载页面时,类名被正确分配,但在刷新页面后,类名不再匹配,因此组件将丢失其样式。这是我在控制台上收到的错误消息:

警告:道具
className
不匹配。 服务器:“MuiFormControl-root-3 MuiFormControl-marginNormal-4 搜索栏-textField-31“ 客户:“MuiFormControl-root-3 MuiFormControl-marginNormal-4 搜索栏-textField-2

我关注了MaterialUITextField及其附带的内容,但我似乎不知道是什么导致了服务器和客户端类名之间的差异

在添加带有删除“x”图标的材质UI芯片时,我遇到了类似的问题。刷新后,“x”图标呈现出惊人的1024px宽度。相同的基本问题是icon没有收到正确的样式类

关于堆栈溢出,有几个问题可以解决为什么客户端和服务器可能以不同方式呈现类名(例如,需要升级到@Material UI/core version^1.0.0,使用自定义server.js,并在setState中使用Math.random),但这些问题都不适用于我的情况

我不知道是否会有帮助,但可能不会,因为他们使用的是MaterialUI的测试版

复制的最小步骤: 创建项目文件夹并启动节点服务器:

mkdir app
cd app
npm init -y
npm install react react-dom next @material-ui/core
npm run dev
编辑package.json: 添加到“脚本”:“开发”:“下一步”,

app/pages/index.jsx:
从“下一个/头”导入头
从“@material ui/core/CssBaseline”导入CssBaseline
从“./组件/搜索栏”导入搜索栏
常数索引=()=>(
)
导出默认索引
app/components/SearchBar.jsx:
从“道具类型”导入道具类型
从“@material ui/core/styles”导入{withStyles}”
从“@material ui/core/TextField”导入TextField
常量样式=(主题)=>({
容器:{
显示:“flex”,
柔性包装:“包装”,
},
文本字段:{
页边距:theme.space.unit/2,
宽度:200,
边框:“2件纯红”,
},
})
类SearchBar扩展了React.Component{
建造师(道具){
超级(道具)
this.state={value:}
this.handleChange=this.handleChange.bind(this)
this.handleSubmit=this.handleSubmit.bind(this)
}
手变(活动){
this.setState({value:event.target.value})
}
handleSubmit(事件){
event.preventDefault()
}
render(){
const{classes}=this.props
返回(
)
}
}
SearchBar.propTypes={
类:PropTypes.object.isRequired,
}
导出默认样式(样式)(搜索栏)
访问浏览器中的页面
localhost:3000
,查看以下内容:

刷新浏览器并查看以下内容:

请注意,TextField周围的红色边框将消失

相关LIB:
  • “反应”:16.4.0
  • “反应dom”:16.4.0
  • “下一步”:6.0.3
  • “@material ui/core”:1.2.0

问题在于服务器端生成类名,但HTML中不会自动包含样式表。您需要显式提取CSS并将其附加到服务器端呈现组件的UI中。这里解释了整个过程:

我在Next.js和styled组件上遇到了同样的问题,巴贝尔的翻译也遇到了同样的问题。实际上,客户端和服务器端的类名是不同的

在您的.lrc中写入以下内容以修复此问题:

{
"presets": ["next/babel"],
"plugins": [
    [
      "styled-components",
      { "ssr": true, "displayName": true, "preprocess": false }
    ]
]

}

此问题与使用包含ID的动态类名的MUI有关。来自服务器端呈现CSS的ID与客户端CSS的ID不同,因此出现不匹配错误。一个好的开始是阅读

如果您对nextjs有此问题(正如我所做的),请遵循MUI团队提供的示例,可以在此处找到:

最重要的部分在“examples/nextjs/pages/_app.js”中:

相关票证可在此处找到:


它所做的是删除服务器端呈现的样式表,并用新的客户端呈现的样式表替换它。在我的例子中,问题的发生是因为客户端代码和服务器端的webpack编译模式不同:客户端包是由webpack使用“生产”模式生成的,而服务器从一个为“开发”而优化的包中运行了一些SSR代码。这在generateAndInjectStyles()中的样式化组件中创建了不同的“className”哈希:


因此,我的修复方法只是对齐webpack模式。

问题在于Next.js中的SSR呈现,它在呈现页面之前生成样式片段

使用Material UI和Next.js(正如作者所使用的),添加名为
\u document.js的文件解决了这个问题

调整后的
\u document.js
():

从“React”导入React;
从“next/Document”导入文档,{Html,Head,Main,NextScript};
从“@material ui/styles”导入{ServerStyleSheets};//如果您喜欢使用@material ui/core/styles,则可以使用它。
从“../src/theme”;/”导入主题也在这里调整
导出默认类MyDocument扩展文档{
render(){
返回(
{/*不完全需要,但这是PWA原色*/}
);
}
}
//`getInitialProps`属于`U文档`(而不是`U应用`),
//它与服务器端生成(SSG)兼容。
MyDocument.getInitialProps=async(ctx)=>{
//决议顺序
//
//在服务器上:
//1.app.getInitialProps
//2.page.getInitialProps
//3.document.getInitialProps
//4.app.render
//5.page.render
//6.document.render
//
//在服务器上,出现以下错误:
//1.document.getInitialProp
import PropTypes from "prop-types"
import { withStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"

const styles = (theme) => ({
  container: {
    display: "flex",
    flexWrap: "wrap",
  },
  textField: {
    margin: theme.spacing.unit / 2,
    width: 200,
    border: "2px solid red",
  },
})

class SearchBar extends React.Component {
  constructor(props) {
    super(props)
    this.state = { value: "" }
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  handleChange(event) {
    this.setState({ value: event.target.value })
  }

  handleSubmit(event) {
    event.preventDefault()
  }

  render() {
    const { classes } = this.props
    return (
      <form
        className={classes.container}
        noValidate
        autoComplete="off"
        onSubmit={this.handleSubmit}
      >
        <TextField
          id="search"
          label="Search"
          type="search"
          placeholder="Search..."
          className={classes.textField}
          value={this.state.value}
          onChange={this.handleChange}
          margin="normal"
        />
      </form>
    )
  }
}

SearchBar.propTypes = {
  classes: PropTypes.object.isRequired,
}

export default withStyles(styles)(SearchBar)
{
"presets": ["next/babel"],
"plugins": [
    [
      "styled-components",
      { "ssr": true, "displayName": true, "preprocess": false }
    ]
]
componentDidMount() {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }
if (process.env.NODE_ENV !== 'production') dynamicHash = phash(dynamicHash, partRule + i);
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@material-ui/styles'; // works with @material-ui/core/styles, if you prefer to use it.
import theme from '../src/theme'; // Adjust here as well

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          {/* Not exactly required, but this is the PWA primary color */}
          <meta name="theme-color" content={theme.palette.primary.main} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
  // Resolution order
  //
  // On the server:
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. document.getInitialProps
  // 4. app.render
  // 5. page.render
  // 6. document.render
  //
  // On the server with error:
  // 1. document.getInitialProps
  // 2. app.render
  // 3. page.render
  // 4. document.render
  //
  // On the client
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. app.render
  // 4. page.render

  // Render app and page and get the context of the page with collected side effects.
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,
    // Styles fragment is rendered after the app and page rendering finish.
    styles: [...React.Children.toArray(initialProps.styles), sheets.getStyleElement()],
  };
};
Warning: Prop `className` did not match. Server: "jss3" Client: "App-colNav-3"
    "devServer": "webpack --config webpack.server.config.js --mode=production --watch",
    "devClient": "webpack --mode=development --watch",
    "devServer": "webpack --config webpack.server.config.js --mode=development --watch",
    "devClient": "webpack --mode=development --watch",