Dav*_*vid 8 node.js reactjs material-ui nextjs
I'm having difficulty with differences between client-side and server-side rendering of styles in Material-UI components due to classNames being assigned differently.
The classNames are assigned correctly on first loading the page, but after refreshing the page, the classNames no longer match so the component loses its styling. This is the error message I am receiving on the Console:
Warning: Prop
classNamedid not match. Server: "MuiFormControl-root-3 MuiFormControl-marginNormal-4 SearchBar-textField-31" Client: "MuiFormControl-root-3 MuiFormControl-marginNormal-4 SearchBar-textField-2"
I've followed the Material-UI TextField example docs, and their accompanying Code Sandbox example, but I can't seem to figure out what is causing the difference between the server and client classNames.
I experienced a similar issue when adding Material-UI Chips with a delete 'x' icon. The 'x' icon rendered with a monstrous 1024px width after refreshing. The same underlying issue being that icon was not receiving the correct class for styling.
There are a few questions on Stack Overflow addressing why the client and server might render classNames differently (e.g. need to upgrade to @Material-UI/core version ^1.0.0, using a custom server.js, and using Math.random in setState), but none of these apply in my case.
I don't know enough to tell whether this Github discussion might help, but likely not since they were using a beta version of Material-UI.
Create project folder and start Node server:
mkdir app
cd app
npm init -y
npm install react react-dom next @material-ui/core
npm run dev
Run Code Online (Sandbox Code Playgroud)
Add to 'scripts': "dev": "next",
import Head from "next/head"
import CssBaseline from "@material-ui/core/CssBaseline"
import SearchBar from "../components/SearchBar"
const Index = () => (
<React.Fragment>
<Head>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
</Head>
<CssBaseline />
<SearchBar />
</React.Fragment>
)
export default Index
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
在浏览器中访问页面localhost:3000并看到以下内容:
刷新浏览器,然后看到以下内容:
请注意,TextField周围的红色边框消失了。
Leo*_*lva 42
问题在于 Next.js 中的 SSR 渲染,它在渲染页面之前生成样式片段。
使用 Material UI 和 Next.js(作者正在使用),添加一个名为 _document.js解决了问题。
调整后_document.js(如此处建议):
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()],
};
};
Run Code Online (Sandbox Code Playgroud)
chr*_*web 23
此问题与使用包含 ID 的动态类名的 MUI 相关。来自服务器端呈现的 CSS 的 ID 与客户端 CSS 的 ID 不同,因此会出现不匹配错误。一个好的开始是阅读MUI SSR 文档
如果你对 nextjs 有这个问题(就像我一样)按照 MUI 团队提供的例子,可以在这里找到:material-ui/examples/nextjs
最重要的部分在“examples/nextjs/pages/_app.js”中:
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}
Run Code Online (Sandbox Code Playgroud)
相关票证可以在这里找到:mui-org/material-ui/issues/15073
它的作用是删除服务器端呈现的样式表并将其替换为新的客户端呈现的样式表
Dha*_*amy 19
问题是服务器端生成类名,但样式表不会自动包含在 HTML 中。您需要显式提取 CSS 并将其附加到服务器端呈现组件的 UI。整个过程在这里解释:https : //material-ui.com/guides/server-rendering/
Edw*_*Lee 11
我在Material-ui V5上遇到了这个问题。解决此问题的解决方案是确保类名生成器在服务器和客户端上的行为相同。因此,在您的 _app.js 中添加以下代码:
import { StylesProvider, createGenerateClassName } from '@mui/styles';
const generateClassName = createGenerateClassName({
productionPrefix: 'c',
});
export default function MyApp(props) {
return <StylesProvider generateClassName={generateClassName}>...</StylesProvider>;
}Run Code Online (Sandbox Code Playgroud)
我在使用 Next.js 和样式组件时遇到了同样的问题,使用 Babel 进行了转译。实际上,客户端和服务器端的类名是不同的。
修复它在你的 .babelrc 中写这个:
{
"presets": ["next/babel"],
"plugins": [
[
"styled-components",
{ "ssr": true, "displayName": true, "preprocess": false }
]
]
Run Code Online (Sandbox Code Playgroud)
}
这里还有另一个重要的单独问题:Material UI is not React Strict Mode compatible。随着Emotion 风格引擎的采用,严格的模式兼容性计划在第 5 版中实现。
在此之前,请确保禁用 React Strict 模式。如果您使用 Next.js,除非您在 next.config.js 中启用它,否则默认情况下这是关闭的。
小智 5
// 1 . Warning: prop classname did not match. Material ui with React Next.js
// 2 . Use your customization css here
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
title: {
flexGrow: 1,
},
my_examle_classssss: {
with: "100%"
}
}));
// 3 . Here my Component
const My_Example_Function = () => {
const classes = useStyles();
return (
<div className={classes.root}>
<Container>
<Examle_Component> {/* !!! Examle_Component --> MuiExamle_Component*/}
</Examle_Component>
</Container>
</div>
);
}
export default My_Example_Function
// 4. Add name parameter to the makeStyles function
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
title: {
flexGrow: 1,
},
my_examle_classssss: {
with: "100%"
},
}), { name: "MuiExamle_ComponentiAppBar" });
{/* this is the parameter you need to add { name: "MuiExamle_ComponentiAppBar" } */ }
{/* The problem will probably be resolved if the name parameter matches the first className in the Warning: you recive..
EXAMPLE :
Warning: Prop `className` did not match.
Server: "MuiSvgIcon-root makeStyles-root-98"
Client: "MuiSvgIcon-root makeStyles-root-1"
The name parameter will be like this { name: "MuiSvgIcon" }
*/ }Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3319 次 |
| 最近记录: |