在 NEXTJS 中警告:期望服务器 HTML 在 <div> 中包含匹配的 <div>

Bob*_*aev 16 typescript reactjs material-ui server-side-rendering next.js

我正在使用 nextjs 和 mui。我在渲染页面时遇到警告。这是我的代码。请帮忙解决问题!!!

\n
import "../styles/globals.scss";\nimport { AppProps } from "next/app";\nimport useGetAuthentication from "../hooks/useGetAuthentication";\nimport States from "../interfaces/states";\nimport STATUS from "../constants/status";\nimport { CssBaseline } from "@mui/material";\nimport { ThemeProvider } from "@mui/material/styles";\nimport theme from "../styles/theme";\nimport Layout from "../layouts/Layout";\nimport Head from "next/head";\nimport * as React from "react";\nimport Login from "../components/Login";\nimport { Box } from "@mui/material";\n\ninterface MyAppProps extends AppProps {\n  emotionCache?: EmotionCache;\n}\n\nconst checkStatusCode = (statusCode: number): boolean => {\n  return statusCode === STATUS.NOT_FOUND || statusCode === STATUS.INTERNAL_SERVER_ERROR;\n};\n\nfunction App({ Component, pageProps }: AppProps) {\n  const { states } = pageProps;\n  const { statusCode } = pageProps;\n  const { isAuthorized } = useGetAuthentication(states as States);\n\n  console.log("App -> Component", Component);\n  console.log("App -> pageProps", pageProps);\n  console.log("App -> states", states);\n  console.log("App -> statusCode", statusCode);\n  const drawerWidth: number = 240;\n  if (!checkStatusCode(statusCode) && !isAuthorized)\n    return (\n      <Box\n        component="main"\n        sx={{\n          flexGrow: 1,\n          p: 3,\n          width: { lg: "230px", sm: `calc(100% - ${drawerWidth}px)` }\n        }}\n      >\n        <Login />\n      </Box>\n    );\n\n  return (\n    <>\n      <Head>\n        <meta name="viewport" content="initial-scale=1, width=device-width" />\n      </Head>\n      <ThemeProvider theme={theme}>\n        <CssBaseline />\n        <Layout>\n          <Component {...pageProps} />\n        </Layout>\n      </ThemeProvider>\n    </>\n  );\n}\n\nexport default App;\n
Run Code Online (Sandbox Code Playgroud)\n

登录组件如下

\n
    import React, { useEffect } from "react";\nimport authenticationStore from "../../stores/persistences/authenticationStore";\nimport TestHttp from "../../httpModules/testHttp";\nimport STATUS from "../../constants/status";\nimport RequestSignIn from "../../interfaces/test/requestSignIn";\nimport styles from "./login.module.scss";\nimport { Alert, FormControlLabel, Grid, Paper, TextField, Typography, Stack, Button, Checkbox } from "@mui/material";\nimport Image from "next/image";\nimport useLoginInputs from "../../hooks/useLoginInputs";\nimport LocalStorageHandler from "../../utils/localStorageHandler";\nimport RememberId from "../../interfaces/rememberId";\nimport ERROR_MESSAGE from "../../constants/errorMessage";\nimport LOGIN_INFO from "../../constants/loginInfo";\n\nconst Login: React.FC = () => {\n  const localStorageHandler = new LocalStorageHandler<RememberId>();\n  const authorize = authenticationStore((state) => state.authorize);\n  const testHttp = new TestHttp();\n  const { inputs, setInputs, isRememberChecked, isError, setIsError, isIdEmpty, setIsIdEmpty, isPasswordEmpty, setIsPasswordEmpty, inputsHandler, checkboxHandler } = useLoginInputs([\n    "id",\n    "password"\n  ]);\n\n  const { id, password } = inputs;\n\n  const onLoginHandler = async (): Promise<void> => {\n    if (isIdEmpty) {\n      return setIsError(ERROR_MESSAGE.ID_EMPTY);\n    }\n    if (isPasswordEmpty) {\n      return setIsError(ERROR_MESSAGE.PASSWORD_EMPTY);\n    }\n    const signInInfo: RequestSignIn = { id, password };\n    const { statusCode, jsonResult } = await testHttp.signIn(false, signInInfo);\n\n    /*\n     *   \xec\x9d\xb8\xec\xa6\x9d \xec\x8b\xa4\xed\x8c\xa8 (\xec\x95\x84\xec\x9d\xb4\xeb\x94\x94, \xeb\xb9\x84\xeb\xb0\x80\xeb\xb2\x88\xed\x98\xb8 \xec\x9d\xbc\xec\xb9\x98 \xed\x95\x98\xec\xa7\x80 \xec\x95\x8a\xeb\x8a\x94 \xea\xb2\xbd\xec\x9a\xb0 \xeb\x93\xb1) \xeb\xb0\x9c\xec\x83\x9d \xec\x8b\x9c \xec\xbd\x94\xeb\x93\x9c \xec\x9e\x91\xec\x84\xb1\n     */\n\n    if (statusCode !== STATUS.OK) {\n      setIsError(true);\n      return;\n    }\n    const { userInfo, tokenInfo } = jsonResult;\n    authorize(statusCode, userInfo, tokenInfo);\n    if (!isRememberChecked) return localStorageHandler.removeLocalStorageData(LOGIN_INFO.REMEMBER_ID);\n    localStorageHandler.setLocalStorageData("rememberId", {\n      id\n    });\n  };\n\n  useEffect(() => {\n    console.log("\xed\x95\x98\xec\x9d\xb4");\n    setIsIdEmpty(id.length <= 0);\n    setIsPasswordEmpty(password.length <= 0);\n    setIsError(null);\n  }, [id, password]);\n\n  console.log("id", id);\n  console.log("password", password);\n  return (\n    <Grid>\n      <Paper elevation={10} className={styles.container}>\n        <Grid align={"center"}>\n          <div className={styles.logo}>\n            <Image src={"/images/logo.svg"} width={"200px"} height={"80px"} alt={"logo"} />\n            <Typography variant={"h6"}>\xea\xb4\x80\xeb\xa6\xac\xec\x9e\x90</Typography>\n          </div>\n        </Grid>\n        <Stack spacing={1} justifyContent={"center"} alignItems={"center"} className={styles["login-container"]}>\n          <TextField name={"id"} placeholder={"\xec\x95\x84\xec\x9d\xb4\xeb\x94\x94\xeb\xa5\xbc \xec\x9e\x85\xeb\xa0\xa5\xed\x95\xb4\xec\xa3\xbc\xec\x84\xb8\xec\x9a\x94."} required value={id} type={"text"} className={styles["login-input"]} onChange={inputsHandler} />\n          <TextField name={"password"} placeholder={"\xeb\xb9\x84\xeb\xb0\x80\xeb\xb2\x88\xed\x98\xb8\xeb\xa5\xbc \xec\x9e\x85\xeb\xa0\xa5\xed\x95\x98\xec\x84\xb8\xec\x9a\x94."} required value={password} type={"password"} className={styles["login-input"]} onChange={inputsHandler} />\n        </Stack>\n        <Stack>\n          <FormControlLabel control={<Checkbox checked={isRememberChecked} />} label={"\xec\x95\x84\xec\x9d\xb4\xeb\x94\x94 \xec\xa0\x80\xec\x9e\xa5"} className={styles.checkbox} onChange={checkboxHandler} />\n        </Stack>\n        <Stack justifyContent={"center"} alignItems={"center"}>\n          <Button type={"submit"} color={"primary"} variant={"contained"} className={styles["login-button"]} size={"large"} onClick={onLoginHandler}>\n            \xeb\xa1\x9c\xea\xb7\xb8\xec\x9d\xb8\n          </Button>\n        </Stack>\n        <Stack justifyContent={"center"} alignItems={"center"} className={styles["error-message"]}>\n          <div>\n            {isError && (\n              <Alert severity={"error"}>\n                <strong>{isError}</strong>\n              </Alert>\n            )}\n          </div>\n        </Stack>\n      </Paper>\n    </Grid>\n  );\n};\n\nexport default Login;\n
Run Code Online (Sandbox Code Playgroud)\n

警告是

\n
Warning: Expected server HTML to contain a matching <div> in <div>.\n    at div\n    at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-cbed451f.browser.esm.js:57:66)\n    at Box (webpack-internal:///./node_modules/@mui/system/esm/createBox.js:36:72)\n    at Layout (webpack-internal:///./layouts/Layout/index.tsx:16:26)\n    at InnerThemeProvider (webpack-internal:///./node_modules/@mui/system/esm/ThemeProvider/ThemeProvider.js:21:70)\n    at ThemeProvider (webpack-internal:///./node_modules/@mui/private-theming/ThemeProvider/ThemeProvider.js:47:5)\n    at ThemeProvider (webpack-internal:///./node_modules/@mui/system/esm/ThemeProvider/ThemeProvider.js:41:5)\n    at App (webpack-internal:///./pages/_app.tsx:61:27)\n    at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:20638)\n    at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/client.js:8:23179)\n    at Container (webpack-internal:///./node_modules/next/dist/client/index.js:323:9)\n    at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:820:26)\n    at Root (webpack-internal:///./node_modules/next/dist/client/index.js:944:27)\nwindow.console.error @ next-dev.js?3515:25\n
Run Code Online (Sandbox Code Playgroud)\n

bas*_*rat 12

情况1

很可能是一个Server<>Client过时的问题。

使固定

  • 如果您正在使用开发服务器 > 重新启动它。
  • 如果您正在获得此产品>重建+重新启动。

案例2

您使用的组件在服务器 (SSR) 与客户端 (CSR) 上的渲染方式不同(由于编码错误)。可以通过添加suppressHydrationWarning={true}有问题的组件来消除这种情况。

案例3

我见过的另一个例子是有人设置了dangerouslySetInnerHtml无效的 HTML。修复方法是更正 HTML 或将其静音,就像我们在情况 2 中所做的那样。


azw*_*bar 12

使用钩子进行的正在进行的值更新也可能导致此问题。为了防止这种情况,只需将变量值从钩子复制到 useEffect 钩子的状态即可。这是例子

import { useAccount } from "wagmi";

...

  const { address, isConnected, isConnecting } = useAccount();
  // the value of these variable above are changed dynamically
  
  const [connectionStat, setConnectionStat] = useState();
  const [addr, setAddr] = useState();

  // copy the value to state here
  useEffect(() => {
    setConnectionStat(isConnected);
    setAddr(address);
  }, [address, isConnected])

  // then now we can display the value properly

  return (
    <div>
     <p>Connection status :  {connectionStat}</p>
     <p>Connected to      :  {addr}</p>
      ....
    </div>
  );
Run Code Online (Sandbox Code Playgroud)

说明:直接从钩子传递值到 JSX/view 可能会导致 JSX/view 内部的值不一致,因此页面渲染也不一致,并且 SSR 和客户端中的值之间会有不同的值。

希望能帮助到你。


Gre*_*Woz 7

我遇到的真正问题是 LastPass 扩展将该死的 DIV 元素注入到我的代码中。像这样的东西:

<div data-lastpass-icon-root="true" style="position: relative !important; height: 0px !important; width: 0px !important; float: left !important;"></div>
Run Code Online (Sandbox Code Playgroud)

这显然导致服务器和客户端具有不同的节点结构。一旦将 localhost 排除在 LastPass 之外(这并不容易!),错误就消失了。