![](/img/trans.png)
[英]nextjs: Warning: Prop `className` did not match. Server: "x" Client: "y"
[英]Class name hydration mismatch in server and client, (Warning: Prop `className` did not match Server and Client)
我不知道我的問題是一個錯誤,或者只是一個支持條款,或者只是一個配置錯誤,但我花了很多時間,不高興去幸運地寫到這里來解決我的問題。 不!
我搜索了 3 天,這 3 天甚至只睡了大約 10 個小時,嘗試了很多方法,即使我使用Gitter但沒有人回答我,我看到問題#10982和#11506以及許多Stack Overflow
問題和答案,但我不能' t 解決了我的問題,但沒有找到正確的方法。
我絕對閱讀了很多次Material-UI
Docs,但我完全按照 Docs 所說的去做。
最后,我在服務器和客戶端看到類名 hydration 不匹配,😞 這個警告:
Warning: Prop `className` did not match. Server: "MuiFormLabel-root-134 MuiInputLabel-root-129 MuiInputLabel-formControl-130 MuiInputLabel-animated-133" Client: "MuiFormLabel-root-10 MuiInputLabel-root-5 MuiInputLabel-formControl-6 MuiInputLabel-animated-9"
我發誓我嘗試了很多方法並搜索了很多。 我正在使用Material-UI
版本1.2.1
。
這是我作為根組件的Index
組件:
import React, {Component} from 'react';
import Helmet from 'react-helmet';
import styles from 'StylesRoot/styles.pcss';
import TextField from '@material-ui/core/TextField';
export default class App extends Component {
render() {
return (
<div className={styles['root-wrapper']}>
<Helmet
htmlAttributes={{lang: 'fa', amp: undefined}}
bodyAttributes={{dir: 'rtl'}}
titleTemplate='اسکن - %s'
titleAttributes={{itemprop: 'name', lang: 'fa'}}
meta={[
{name: 'description', content: 'صفحه اتصال اعضاء'},
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
]}
link={[{rel: 'stylesheet', href: '/dist/styles.css'}]}
/>
<TextField label='test' helperText='help'/>
</div>
);
};
}
在下面你可以看到我的server.jsx
和client.jsx
:
//--------------------------------------------client.jsx
import React from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {MuiThemeProvider, createMuiTheme} from '@material-ui/core/styles';
import {lightBlue, red} from '@material-ui/core/colors';
import Index from './app/index';
import RTL from './app/public/rtl';
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
class Main extends React.Component {
// Remove the server-side injected CSS.
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render() {
return (
<BrowserRouter>
<Index {...this.props}/>
</BrowserRouter>
);
}
}
hydrate((
<RTL>
<MuiThemeProvider theme={theme}>
<Main/>
</MuiThemeProvider>
</RTL>
), document.getElementById('root'));
//--------------------------------------------server.jsx
import React from 'react';
import {renderToString} from 'react-dom/server';
import {SheetsRegistry} from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import {MuiThemeProvider, createMuiTheme, createGenerateClassName} from '@material-ui/core/styles';
import {red, lightBlue} from '@material-ui/core/colors';
import Template from './app/template';
import Index from './app/index';
import RTL from './app/public/rtl';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const sheetsRegistry = new SheetsRegistry();
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const generateClassName = createGenerateClassName();
const markup = renderToString(
<JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
<RTL>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<StaticRouter location={req.url} context={context}>
<Index/>
</StaticRouter>
</MuiThemeProvider>
</RTL>
</JssProvider>
);
const helmet = Helmet.renderStatic();
const jss = sheetsRegistry.toString();
res.status(200).send(Template({
markup: markup,
helmet: helmet,
jss: jss,
}));
};
}
所以現在需要template.jsx
:
export default ({ markup, helmet, jss }) => {
return `<!DOCTYPE html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<style id='jss-server-side'>${jss}</style>
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id='root'>${markup}</div>
<script src='/dist/client.js' async></script>
</body>
</html>`;
};
好的,現在有可能會問這個問題What is RTL
? 為什么你在服務器和客戶端都將MuiThemeProvider
包裹在里面?
所以RTL
組件:
import React from 'react';
import {create} from 'jss';
import rtl from 'jss-rtl';
import JssProvider from 'react-jss/lib/JssProvider';
import {createGenerateClassName, jssPreset} from '@material-ui/core/styles';
const jss = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
export default props => (
<JssProvider jss={jss} generateClassName={generateClassName}>
{props.children}
</JssProvider>
);
我在Material-UI
Docs 中看到了它,我相信文檔有點差,可以改進。 我問自己,為什么文檔props.children
為這個函數傳遞一個props.children
? 我想也許這個函數應該包裝一些東西。 所以我嘗試了許多形狀並且它有效。 但是在構建后在瀏覽器中的第一次調用中。 當我刷新瀏覽器時會出現警告😞,我看到了這個該死的形狀:
我當然想看到下面的形狀,但我在構建后只看到一次:
我不知道出了什么問題。 我也在Github
上留下了一個問題。 並為此問題上傳一個迷你存儲庫,以便查看我的問題,只需pull
,然后運行npm install
然后num run dev
。 該項目可在localhost:4000上訪問
我不知道這種方式是否合適,但效果很好,實際上,我發現使用單獨的RTL
Component並不是一個好方法,這導致服務器和客戶端之間的不一致,我使用了Right-to -為每個服務器和客戶端分別Rtl.jsx
文檔頁面,因此省略了Rtl.jsx
文件和它的組件從我的項目,所以, server.jsx
和client.jsx
如下所示:
//---------------------------------------------client.jsx
import React, {Component} from 'react';
import {hydrate} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {
MuiThemeProvider, createMuiTheme,
createGenerateClassName, jssPreset
} from '@material-ui/core/styles';
import {create} from 'jss';
import rtl from 'jss-rtl';
import JssProvider from 'react-jss/lib/JssProvider';
import {lightBlue, red} from '@material-ui/core/colors';
import Index from './app/index';
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const jssRTL = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
class Main extends Component {
componentDidMount() {
const jssStyles = document.getElementById('jss-server-side');
if (jssStyles) {
jssStyles.remove();
}
}
render() {
return (
<BrowserRouter>
<Index {...this.props}/>
</BrowserRouter>
);
}
}
hydrate((
<JssProvider jss={jssRTL} generateClassName={generateClassName}>
<MuiThemeProvider theme={theme}>
<Main/>
</MuiThemeProvider>
</JssProvider>
), document.getElementById('root'));
//---------------------------------------------server.jsx
import React from 'react';
import {renderToString} from 'react-dom/server';
import {SheetsRegistry} from 'react-jss/lib/jss';
import JssProvider from 'react-jss/lib/JssProvider';
import {StaticRouter} from 'react-router-dom';
import {Helmet} from "react-helmet";
import {
MuiThemeProvider, createMuiTheme,
createGenerateClassName, jssPreset
} from '@material-ui/core/styles';
import {create} from 'jss';
import rtl from 'jss-rtl';
import {red, lightBlue} from '@material-ui/core/colors';
import Template from './app/template';
import Index from './app/index';
export default function serverRenderer({clientStats, serverStats}) {
return (req, res, next) => {
const context = {};
const sheetsRegistry = new SheetsRegistry();
const theme = createMuiTheme({
palette: {
primary: lightBlue,
accent: red,
type: 'light',
},
direction: 'rtl',
});
const jssRTL = create({plugins: [...jssPreset().plugins, rtl()]});
const generateClassName = createGenerateClassName();
const markup = renderToString(
<JssProvider jss={jssRTL}
registry={sheetsRegistry}
generateClassName={generateClassName}>
<MuiThemeProvider theme={theme} sheetsManager={new Map()}>
<StaticRouter location={req.url} context={context}>
<Index pathname={req.url}/>
</StaticRouter>
</MuiThemeProvider>
</JssProvider>
);
const helmet = Helmet.renderStatic();
const jss = sheetsRegistry.toString();
res.status(200).send(Template({
markup,
helmet,
jss,
}));
};
}
它在雙方、服務器和客戶端都能很好地工作,並在樣式標簽中制作一致的Material-UI
CSS
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.