sourcecode

반응 + 재료 UI - 경고: Prop className이 일치하지 않습니다.

copyscript 2023. 3. 20. 23:25
반응형

반응 + 재료 UI - 경고: Prop className이 일치하지 않습니다.

classNames가 다르게 할당되어 있어 Material-UI 컴포넌트 스타일의 클라이언트 측 렌더링과 서버 측 렌더링의 차이가 있습니다.

페이지를 처음 로드할 때는 classNames가 올바르게 할당되지만 페이지를 새로 고친 후에는 classNames가 일치하지 않게 되어 컴포넌트의 스타일이 없어집니다.콘솔에 표시되는 오류 메시지는 다음과 같습니다.

:: " " "className일치하지 않습니다.서버: "MuiFormControl-root-3 MuiFormControl-marginNormal-4 SearchBar-textField-31" 클라이언트: "MuiFormControl-root-3 MuiFormControl-marginNormal-4 SearchBar-TextField-2"

Material-UI TextField의 문서와 그에 부수되는 Code Sandbox의 예를 살펴보았지만 서버와 클라이언트의 classNames의 차이점을 찾을 수 없습니다.

삭제 'x' 아이콘이 있는 Material-UI Chips를 추가했을 때도 같은 문제가 발생하였습니다.새로 고침 후 1024px의 엄청난 너비로 렌더링된 'x' 아이콘입니다.같은 근본적인 문제는 아이콘이 올바른 스타일링 클래스를 수신하지 않았다는 것입니다.

스택 오버플로우에서는 클라이언트와 서버가 classNames를 다르게 렌더링하는 이유에 대해 몇 가지 질문이 있습니다(예를 들어 @Material-UI/core 버전 ^1.0.0으로 업그레이드하여 커스텀 server.js를 사용하고 setState에서 Math.random을 사용해야 합니다).이러한 질문들은 모두 해당되지 않습니다.

Github의 논의가 도움이 될지는 모르겠지만, 그들이 Material-UI의 베타 버전을 사용하고 있었기 때문에 도움이 되지 않을 것 같습니다.

재현을 위한 최소 절차:

프로젝트 폴더를 만들고 노드 서버를 시작합니다.

mkdir app
cd app
npm init -y
npm install react react-dom next @material-ui/core
npm run dev

패키지를 편집합니다.json:

에 추가: '스크립트' 추가:"dev": "next",

app/pages/index.dex:

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

app/컴포넌트/SearchBar.jsx:

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)

의 [Visit]localhost:3000이치노

TextField 구성 요소 주위의 빨간색 테두리

브라우저를 새로 고치고 다음을 확인합니다.

TextField 구성 요소의 스타일이 사라졌습니다.

TextField 주변의 빨간색 테두리가 사라집니다.

관련 립스:

  • '표준' : 16.4.0
  • "dom" : 16.4.0
  • '다음' : 6.0.3
  • "@material-ui/core" : 1.2.0

문제는 Next.js의 SSR 렌더링으로, 페이지가 렌더링되기 전에 스타일 조각이 생성됩니다.

와 Next하고 있는 ), 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()],
  };
};

이 문제는 ID가 포함된 다이내믹클래스 이름을 사용하는 MUI와 관련되어 있습니다.서버측에서 렌더링된 CSS의 ID가 클라이언트측 CSS와 동일하지 않기 때문에 미스매치 에러가 발생합니다.MUI SSR 문서를 읽는 것이 좋습니다.

nextjs에 이 문제가 있는 경우(저와 마찬가지로) MUI 팀이 제공하는 예를 따르십시오.Material-ui/examples/nextjs는 다음 URL에서 확인할 수 있습니다.

가장 중요한 부분은 "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);
    }
  }

관련 티켓은 여기서 찾을 수 있습니다.mui-material-ui/mui/15073

서버측 렌더링 스타일시트를 삭제하고 렌더링된 새 클라이언트측으로 대체합니다.

문제는 서버 측에서 클래스 이름을 생성하지만 스타일시트는 자동으로 HTML에 포함되지 않는다는 것입니다.서버 측에서 렌더링된 컴포넌트에 대해 명시적으로 CSS를 추출하여 UI에 추가해야 합니다.자세한 내용은 https://material-ui.com/guides/server-rendering/ 를 참조해 주세요.

또 다른 중요한 문제가 있습니다.Material UI V4는 React Strict Mode와 호환되지 않습니다.Emotion 스타일 엔진을 채택한 버전 5에서는 엄격한 모드 호환성이 예정되어 있습니다.

그때까지 Respect Strict 모드를 디세블로 해 주세요.Next.js를 사용하는 경우 다음을 사용하여 앱을 만든 경우 기본적으로 설정됩니다.create-next-app.

// next.config.js
module.exports = {
  reactStrictMode: false, // or remove this line completely
}

Next.js와 스타일 컴포넌트, Babel에 의한 번역에서도 같은 문제가 있었습니다.실제로 클래스 이름은 클라이언트와 서버 측에서 다릅니다.

.babelrc에 기재하여 수정합니다.

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

이 문제는 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>;
}

// 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" }




*/  }

저는 이 불일치 사례를 공유하고 싶습니다.

경고: next-dev.js? 3515:32 경고: " " "className일치하지 않습니다.서버: "MuiButton-root MuiIconButton-root PrivateSwitchBase-12 MuiSwitch-colorSecondary" 클라이언트: "MuiButtonBase-root MuiSwitch-colorSecondary"

클라이언트에는 2개의 클래스가 더 있습니다.이것은 클라이언트측의 동작이 다르다는 것을 의미합니다.이 경우 이 구성요소는 서버 측에서 렌더링하지 않습니다.해결책은 이 컴포넌트를 동적으로 렌더링하는 것입니다.

export default dynamic(() => Promise.resolve(TheComponent), { ssr: false });

클라이언트와 서버의 classNames가 다른데 문제가 있었습니다.React, Material-UI, makeStyles SSR(서버 사이드 렌더링)사용하고 있었습니다.오류:

Warning: Prop `className` did not match. Server: "jss3" Client: "App-colNav-3"

클라이언트와 서버의 Web pack 모드가 일치하지 않는 것을 깨닫기까지 몇 시간이 걸렸습니다.의 스크립트package.json

    "devServer": "webpack --config webpack.server.config.js --mode=production --watch",
    "devClient": "webpack --mode=development --watch",

내가 둘 다 바꾸면development는 해결되었습니다 mode, "mode" 입니다. : )

    "devServer": "webpack --config webpack.server.config.js --mode=development --watch",
    "devClient": "webpack --mode=development --watch",

만약 누군가가 위의 해결책을 시도했는데도 여전히 어려움을 겪고 있다면, 이것을 시도해 보세요.

  • 「 」를 경우noSsr컴포넌트나 테마의 어느쪽이든 프로포트를 제거합니다.

mui에서는 다음과 같은 설정을 하고 있었습니다.theme오브젝트, 이것이 이 문제의 원인입니다.

import { createTheme, responsiveFontSizes } from "@mui/material/styles";
let theme = createTheme({
  components: {
    MuiUseMediaQuery: {
      defaultProps: {
        noSsr: true,
      },
    },
  },
  palette: {
    mode: "light",
    common: {
      black: "#000",
      white: "#fff",
    },
    primary: {
      main: "#131921",
      contrastText: "#fff",
    },
    secondary: {
      main: "#fb6a02",
      contrastText: "#fff",
    }
   }
})
  • noSSr클라이언트와 서버의 스타일 불일치를 포함한 앱의 모든 문제를 해결했습니다.

이 문제는 Nextjs 서버 측 렌더링이 원인입니다.해결하기 위해 다음과 같이 합니다.

  1. 클라이언트 측에서 검출할 컴포넌트를 만듭니다.
import { useState, useEffect } from "react";

interface ClientOnlyProps {}

// @ts-ignore
const ClientOnly = ({ children }) => {
  const [mounted, setMounted] = useState<boolean>(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  return mounted ? children : null;
};

export default ClientOnly;

  1. 를 사용하여 내 페이지 구성 를 래핑합니다.ClientOnly 표시
export default function App() {
  return (
    <ClientOnly>
      <MyOwnPageComponent>
    </ClientOnly>
  );
}

클라이언트측인 경우는, 페이지상에 컴포넌트만을 렌더링 합니다. 현재 측에서 온 .<MyOwnPageComponent> 아무것도 하지

이 문제는 클라이언트 측 코드와 서버 측 웹 팩의 컴파일 모드가 다르기 때문에 발생했습니다.클라이언트의 번들은 "실가동" 모드를 사용하여 웹 팩에 의해 생성되었으며 서버는 "개발"용으로 최적화된 패키지에서 SSR 코드를 실행했습니다.이로 인해 generateAnd의 스타일 컴포넌트에 다른 "className" 해시가 생성되었습니다.Inject Styles():

if (process.env.NODE_ENV !== 'production') dynamicHash = phash(dynamicHash, partRule + i);

그래서 제 수정은 웹 팩 모드를 맞추는 것이었습니다.

을 붙일 수 있는 곳은 수 .makeStyles 이렇게요.

const useStyles = makeStyles({
  card: {
    backgroundColor: "#f7f7f7",
    width: "33%",
  },
  title: {
    color: "#0ab5db",
    fontWeight: "bold",
  },
  description: {
    fontSize: "1em"
  }
}, { name: "MuiExample_Component" });

어떻게 동작하는지는 모르지만, 여기서 찾았습니다.경고: Prop 'className'이 일치하지 않습니다.재로드 시 Material UI css가 임의로 중단됩니다.

저도 NextJS + MUI v5를 사용하고 있는데 Git 브랜치를 병합한 직후에 이 오류가 발생했습니다.병합으로 인해 캐시의 무언가가 손상된 것 같습니다..next/의 내용을 삭제하고 dev 서버를 재부팅하면 오류가 사라집니다.

@Leonel Sanches da Silva의 답변은 그다지 효과가 없었지만, 다른 (비소재 UI) 프로젝트에서 발견한 단편들을 사용하면 충분히 효과가 있었던 것 같습니다.

dev.to의 Raul Sanchez에게 이 질문에 대한 답을 알려드립니다.

다음 검색 안 함styled-components서버의 스타일을 변경하려면 이 페이지를 다음에 추가해야 합니다.pages/_document.js:

import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
}

이 코드는 업데이트될 수 있으므로 Next의 예에서 최신 정보를 확인하십시오.

언급URL : https://stackoverflow.com/questions/50685175/react-material-ui-warning-prop-classname-did-not-match

반응형