Class 服务器和客户端名称水合作用不匹配,(警告:Prop `className` 与服务器和客户端不匹配)

Class name hydration mismatch in server and client, (Warning: Prop `className` did not match Server and Client)

我不知道我的问题是 bug 还是 support 术语或 configuration mismach,但我花了很多时间,不高兴幸运地在这里写信给某人解决我的问题。不!

我找了3天,这3天竟然只睡了10个小时左右,试了很多方法,连我用了Gitter but no one answer me, I see issues #10982 and #11506和很多Stack Overflow问答都解决不了我的问题,没有找到正确的方法。

当然我读了很多遍 Material-UI Docs 但我完全按照文档所说的去做。

最后,我在服务器和客户端中看到 class name hydration mismatch,这个警告:

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.jsxclient.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? and Why you wrap MuiThemeProvider inside在服务器和客户端?

所以先看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 我想也许这个函数应该包装一些东西。所以我尝试了很多形状,都奏效了。但是在构建后在浏览器中的第一次调用中。当我刷新浏览器时出现警告,我看到这个该死的形状:

当然我想看到下面的形状,但我在构建后只看到一次:

我不知道出了什么问题。我在 Github too. And upload a mini repo for this issue, for seeing my issue just pull, and run npm install then num run dev. the project is accessible on localhost:4000

上留下了一个问题

我不知道这种方式是否合适,但效果很好,实际上,我发现,使用单独的 RTL 组件,不是一个好方法,这会导致服务器之间的不一致和客户端,我分别为每个服务器和客户端使用 Right-to-Left 文档页面,因此省略了 Rtl.jsx 文件及其项目中的组件,因此,server.jsxclient.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