React 18: 水合失败,因为初始UI与服务器端渲染的不匹配。

244

我正在尝试在我的应用程序中让SSR正常工作,但是出现了错误:

因为初始UI与在服务器上渲染的内容不匹配,所以水合失败。

演示代码请点击此处

问题的演示请点击此处(打开开发者工具控制台查看错误):

// App.js

 import React from "react";
    
  class App extends React.Component {

  head() {
    return (
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <meta name="theme-color" content="#000000" />
        <title>React App</title>
      </head>
    );
  }

  body() {
    return (
      <body>
        <div className="App">
          <h1>Client says Hello World</h1>
        </div>
      </body>
    );
  }

  render() {
    return (
      <React.Fragment>
        {this.head()}
        {this.body()}
      </React.Fragment>
    )
  }
}
export default App;

// index.js

import React from "react";
import * as ReactDOM from "react-dom/client";
import { StrictMode } from "react";

import App from "./App";


// const container = document.getElementById("root");
const container = document.getElementsByTagName("html")[0]

ReactDOM.hydrateRoot(
  container,
  <StrictMode>
    <App />
  </StrictMode>
);

演示中显示的HTML模板由后端提供,并使用以下代码生成:

const ReactDOMServer = require('react-dom/server');

const clientHtml = ReactDOMServer.renderToString(
<StrictMode>
    <App />
</StrictMode>
)

// 将clientHtml发送给客户端

我需要动态生成如App类所示的<head></head>和<body></body>部分。


41
大家是否都同意,这个错误信息比没有任何帮助还要糟糕?! - Kraken
这个错误可能是由于像Dashlane或LastPass这样的扩展程序触发的,因为它们在渲染后注入HTML,从而破坏了服务器和客户端HTML结构之间的比较。请参考这里https://stackoverflow.com/a/75490907/5213598。 - undefined
61个回答

0
在我的情况下,我遇到了这个问题,因为我使用了<img />标签而不是Next.js的<Image />标签,因为我不能使用tailwind类与<Image />标签。将<img />标签替换为Next.js的<Image />标签解决了我的问题。然后我用一个div标签包装了<Image />标签,并添加了这些类。

0
对我来说,问题是由于 date-fns 包引起的。在删除使用日期格式化的组件后,仅在部署后才显示的错误消息消失了。希望这可以帮到你。

0

这个错误意味着在 html DOM 树中存在一些语义问题。

例如:<tbody> 直接包含子元素 <div> 而不是 <tr>

因此,通过改进这些问题,我们可以获得正确的可访问性基础。


0

在使用context和localstorage编程时,我在NextJs中遇到了同样的错误,通过在_app中进行检查,问题得以解决。


这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。如果您想在此问题获得新的答案时得到通知,您可以关注此问题。一旦您拥有足够的声望,您还可以添加悬赏以吸引更多关注。- 来自审核 - Lord-JulianXLII

0

我只是从App.js的body中删除了<body> <body/>

import React from 'react';

class App extends React.Component {
  head() {
    return (
      <div>
        <head>
          <meta charSet="utf-8" />
          <meta
            name="viewport"
            content="width=device-width, initial-scale=1, shrink-to-fit=no"
          />
          <meta name="theme-color" content="#000000" />
          <title>React App</title>
        </head>
      </div>
    );
  }

  body() {
    return (
      <>
        <div className="App">
          <h1>Client says Hello World</h1>
        </div>
      </>
    );
  }

  render() {
    return (
      <React.Fragment>
        {this.head()}
        {this.body()}
      </React.Fragment>
    );
  }
}

export default App;

在移除了 <body> <body/> 后,我成功了。


0
在我的情况下,在对列表进行映射之前使用reverse()函数时,我也遇到了类似的错误。

您的回答可以通过提供更多支持信息来改善。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认您的回答是否正确。您可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

0
在我的情况下,无论是使HTML更语义化还是修改_app.tsx以检查是否已加载window,都没有解决问题。
目前唯一的解决方案是降级React版本和React DOM。
我正在使用NextJS。
我切换到了17.0.2版本。

0

使用Next13进行测试。

当我忘记使用next/script而不是html中的script元素时,我遇到了这个问题。这是在layout.tsx文件中,我想在所有页面上应用脚本。

在我的情况下,该脚本正在渲染一个元素,当路由状态改变时,该元素不会被渲染,因此脚本没有加载并导致水合错误。


0
我正在使用 Next.js 的 Chakra UI,在我移除了

标签后,错误消失了。


根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community

0

从NextJS + Chakra UI中发现了一个小问题,请确保永远不要像下面这样做:

NEVER
<a href="https://github.com/xxx" target="_blank" rel="noreferrer">
  <IconButton
    as="a"
    href="#"
    aria-label="GitHub"
    icon={<FaGithub fontSize="1.25rem" />}
  />
</a>

这也会触发上述错误:由于初始 UI 与在服务器上呈现的不匹配,因此水合失败

结论:请确保您编写的代码在调试过程中有效并且有意义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接