警告:文本内容不匹配。服务器:"I'm out" 客户端:"I'm in" div

Warning: Text content did not match. Server: "I'm out" Client: "I'm in" div

我在 Next.js 项目中使用 universal-cookie,这是 return 在控制台中发出警告的简单代码:

import React, { useState } from "react";
import Cookies from "universal-cookie";
import styles from "../styles/Home.module.css";

export default function Home() {
  const cook = new Cookies();
  const [session, setSession] = useState(cook.get("key"));
  const setCookie = () => {
    cook.set("key", "hola", { secure: true });
    setSession(cook.get("key"));
  };
  const deleteCookie = () => {
    cook.remove("key", { secure: true });
    setSession(undefined);
  };

  return (
    <div className={styles.container}>
      <button onClick={() => setCookie()}>Save Cookie</button>
      <button onClick={() => deleteCookie()}>Delete Cookie</button>
      {session ? <>I'm in</> : <>I'm out</>}
    </div>
  );
}

当“I'M IN”然后刷新页面时,控制台中出现以下警告:

我已经到处寻找解决方案。

Next.js pre-renders every page 在服务器上。

This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.

(...) When a page is loaded by the browser, its JavaScript code runs and makes the page fully interactive. (This process is called hydration.)

在浏览器上呈现的 HTML 与在服务器上生成的不匹配,因为两者 cook.get("key") returns 不同。

有几个选项可以解决这个问题。


#1 将设置状态移动到 useEffect

第一个解决方案是将状态设置移动到 useEffect.

export default function Home() {
    const cook = new Cookies();
    const [session, setSession] = useState();
    
    // `setCookie` and `deleteCookie` code here

    useEffect(() => {
        setSession(cook.get("key"));
    }, []);

    return (
        <div className={styles.container}>
            <button onClick={() => setCookie()}>Save Cookie</button>
            <button onClick={() => deleteCookie()}>Delete Cookie</button>
            {session ? <>I'm in</> : <>I'm out</>}
        </div>
    );
}

#2 将 next/dynamic{ ssr: false }

结合使用

作为替代解决方案,也可以通过使用 { ssr: false } 动态导入带有 next/dynamic 的 React 组件来规避该问题,无论该组件在何处使用。这可以防止组件包含在服务器上,并仅在客户端动态加载它。

const Home = dynamic(
    () => import('../components/Home'),
    { ssr: false }
)

在 html 元素(不同)上使用 suppresshydrationwarning 属性对我有用。根据以下文档,它仅在单个级别起作用:

https://reactjs.org/docs/dom-elements.html#suppresshydrationwarning