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

Viewed 4489

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
1 Answers

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.

Related