Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

in NEXTJS Warning: Expected server HTML to contain a matching <div> in <div>

I am using nextjs and mui. I am facing a warning when rendering pages. Here is my code. Please help to solve the issue!!!

import "../styles/globals.scss";
import { AppProps } from "next/app";
import useGetAuthentication from "../hooks/useGetAuthentication";
import States from "../interfaces/states";
import STATUS from "../constants/status";
import { CssBaseline } from "@mui/material";
import { ThemeProvider } from "@mui/material/styles";
import theme from "../styles/theme";
import Layout from "../layouts/Layout";
import Head from "next/head";
import * as React from "react";
import Login from "../components/Login";
import { Box } from "@mui/material";

interface MyAppProps extends AppProps {
  emotionCache?: EmotionCache;
}

const checkStatusCode = (statusCode: number): boolean => {
  return statusCode === STATUS.NOT_FOUND || statusCode === STATUS.INTERNAL_SERVER_ERROR;
};

function App({ Component, pageProps }: AppProps) {
  const { states } = pageProps;
  const { statusCode } = pageProps;
  const { isAuthorized } = useGetAuthentication(states as States);

  console.log("App -> Component", Component);
  console.log("App -> pageProps", pageProps);
  console.log("App -> states", states);
  console.log("App -> statusCode", statusCode);
  const drawerWidth: number = 240;
  if (!checkStatusCode(statusCode) && !isAuthorized)
    return (
      <Box
        component="main"
        sx={{
          flexGrow: 1,
          p: 3,
          width: { lg: "230px", sm: `calc(100% - ${drawerWidth}px)` }
        }}
      >
        <Login />
      </Box>
    );

  return (
    <>
      <Head>
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <Layout>
          <Component {...pageProps} />
        </Layout>
      </ThemeProvider>
    </>
  );
}

export default App;

Login component is below

    import React, { useEffect } from "react";
import authenticationStore from "../../stores/persistences/authenticationStore";
import TestHttp from "../../httpModules/testHttp";
import STATUS from "../../constants/status";
import RequestSignIn from "../../interfaces/test/requestSignIn";
import styles from "./login.module.scss";
import { Alert, FormControlLabel, Grid, Paper, TextField, Typography, Stack, Button, Checkbox } from "@mui/material";
import Image from "next/image";
import useLoginInputs from "../../hooks/useLoginInputs";
import LocalStorageHandler from "../../utils/localStorageHandler";
import RememberId from "../../interfaces/rememberId";
import ERROR_MESSAGE from "../../constants/errorMessage";
import LOGIN_INFO from "../../constants/loginInfo";

const Login: React.FC = () => {
  const localStorageHandler = new LocalStorageHandler<RememberId>();
  const authorize = authenticationStore((state) => state.authorize);
  const testHttp = new TestHttp();
  const { inputs, setInputs, isRememberChecked, isError, setIsError, isIdEmpty, setIsIdEmpty, isPasswordEmpty, setIsPasswordEmpty, inputsHandler, checkboxHandler } = useLoginInputs([
    "id",
    "password"
  ]);

  const { id, password } = inputs;

  const onLoginHandler = async (): Promise<void> => {
    if (isIdEmpty) {
      return setIsError(ERROR_MESSAGE.ID_EMPTY);
    }
    if (isPasswordEmpty) {
      return setIsError(ERROR_MESSAGE.PASSWORD_EMPTY);
    }
    const signInInfo: RequestSignIn = { id, password };
    const { statusCode, jsonResult } = await testHttp.signIn(false, signInInfo);

    /*
     *   인증 실패 (아이디, 비밀번호 일치 하지 않는 경우 등) 발생 시 코드 작성
     */

    if (statusCode !== STATUS.OK) {
      setIsError(true);
      return;
    }
    const { userInfo, tokenInfo } = jsonResult;
    authorize(statusCode, userInfo, tokenInfo);
    if (!isRememberChecked) return localStorageHandler.removeLocalStorageData(LOGIN_INFO.REMEMBER_ID);
    localStorageHandler.setLocalStorageData("rememberId", {
      id
    });
  };

  useEffect(() => {
    console.log("하이");
    setIsIdEmpty(id.length <= 0);
    setIsPasswordEmpty(password.length <= 0);
    setIsError(null);
  }, [id, password]);

  console.log("id", id);
  console.log("password", password);
  return (
    <Grid>
      <Paper elevation={10} className={styles.container}>
        <Grid align={"center"}>
          <div className={styles.logo}>
            <Image src={"/images/logo.svg"} width={"200px"} height={"80px"} alt={"logo"} />
            <Typography variant={"h6"}>관리자</Typography>
          </div>
        </Grid>
        <Stack spacing={1} justifyContent={"center"} alignItems={"center"} className={styles["login-container"]}>
          <TextField name={"id"} placeholder={"아이디를 입력해주세요."} required value={id} type={"text"} className={styles["login-input"]} onChange={inputsHandler} />
          <TextField name={"password"} placeholder={"비밀번호를 입력하세요."} required value={password} type={"password"} className={styles["login-input"]} onChange={inputsHandler} />
        </Stack>
        <Stack>
          <FormControlLabel control={<Checkbox checked={isRememberChecked} />} label={"아이디 저장"} className={styles.checkbox} onChange={checkboxHandler} />
        </Stack>
        <Stack justifyContent={"center"} alignItems={"center"}>
          <Button type={"submit"} color={"primary"} variant={"contained"} className={styles["login-button"]} size={"large"} onClick={onLoginHandler}>
            로그인
          </Button>
        </Stack>
        <Stack justifyContent={"center"} alignItems={"center"} className={styles["error-message"]}>
          <div>
            {isError && (
              <Alert severity={"error"}>
                <strong>{isError}</strong>
              </Alert>
            )}
          </div>
        </Stack>
      </Paper>
    </Grid>
  );
};

export default Login;

the warning is

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

Boburmirzo Muhibullaev


2 Answers

In progress value update from use hook can also causing this problem. To prevent it, just copy the variable value from hook to state from useEffect hook. Here is the example

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>
  );

Explanation: passing value directly from hook to JSX/view may causing inconsistent value inside JSX/view so the the page render can not be consistent as well and there will be different value between value in SSR and client.

Hope it helps.

like image 67
azwar_akbar Avatar answered Nov 28 '25 13:11

azwar_akbar


Case 1

Most likely a Server<>Client out of date issue.

Fix

  • If you are using the development server > Restart it.
  • If you are getting this production > Rebuild + Restart.

Case 2

The components you are using render differently (due to bad coding) on the Server (SSR) vs Client (CSR). This can be silenced by adding suppressHydrationWarning={true} to the offending component.

Case 3

Another case I've seen is that someone has set dangerouslySetInnerHtml with invalid HTML. The fix is to correct the HTML OR silence it as we did in case 2.

like image 20
basarat Avatar answered Nov 28 '25 13:11

basarat