重新混合水合作用失败:服务器和客户端上的 UI 不匹配

2024-07-03

在本地没问题(已知警告并且 CSS 渲染良好),但在 Vercel 上我的 Remix 应用程序收到此错误:

水合失败,因为初始 UI 与原来的 UI 不匹配 在服务器上渲染。

业务逻辑运行良好,但 CSS 完全损坏。

2022 年 6 月 26 日 15:50 更新

我从头开始了一个新项目,并逐一添加依赖项,并在每一步中部署到 Vercel。没有错误。样式化的组件渲染良好。所以依赖关系不是问题。

然后,我开始通过加载器从数据库中逐段获取数据,并将它们一一呈现在样式组件中。始终破坏 CSS 并产生错误的一件事是在渲染之前将日期时间对象转换为字符串:

const DateTimeSpan = styled.span`
  font-size: 1rem;
`;

const hr = now.getHours();
const min = now.getMinutes();

<DateTimeSpan>
  {`${hr}:${min}`}
</DateTimeSpan>

奇怪的是,只有当我将其格式化为仅渲染时间时,它才会中断。有了日期,就可以了:

const yr = now.getFullYear();
const mth = now.getMonth();
const dd = now.getDate();

<DateTimeSpan>
  {`${yr}-${mth}-${dd}`}
<DateTimeSpan>

我无法解释这一点。

更新于 2022 年 7 月 2 日 21:55

使用上面相同的最简单的项目,我和朋友确定当我们尝试渲染时带有样式组件的 CSS 会中断hours, i.e.:

const hr = now.getHours();

<DateTimeSpan>
  {hr}
</DateTimeSpan>

我们怀疑样式组件会损坏,因为小时在服务器上以 UTC 时间呈现,但在客户端上以区域设置时间呈现。

我不确定这是否是一个错误,或者我们是否应该自己处理这个问题。也不确定是否应该在 Remix 或 Styled 组件 GitHub 问题上询问这个问题。我已经开了一个issue https://github.com/remix-run/remix/issues/3645无论如何,也可以从 Remix 开始。

原帖

不确定,但可能与这些问题有关:

  • Remix 2570 https://github.com/remix-run/remix/issues/2570
  • Remix 2947 https://github.com/remix-run/remix/issues/2947
  • React 24523 https://github.com/facebook/react/pull/24523

我通读了上面的内容和其他一些页面,我所能想到的就是更新一些依赖项。以下是可能相关的:

{
"react": "^18.2.0",
"styled-components": "^5.3.5"
"@remix-run/node": "^1.6.1",
"@remix-run/react": "^1.6.1",
"@remix-run/vercel": "^1.6.1",
"@vercel/node": "^2.2.0",
}

我的主要怀疑是它与样式组件有关,因为我之前在 Nextjs 上也遇到过类似的问题。但我的 app/root.tsx 和 app/entry.server.tsx 遵循这个例子 https://github.com/remix-run/remix/tree/dev/examples/styled-components对于样式组件非常接近:

// app/root.tsx

export default function App() {
  const data = useLoaderData();

  return (
    <Html lang="en">
      <head>
        ...
        {typeof document === "undefined" ? "__STYLES__" : null}
      </head>
      <Body>
        ...
      </Body>
    </Html>
  );
}
//app/entry.server.tsx

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const sheet = new ServerStyleSheet();

  let markup = renderToString(
    sheet.collectStyles(
      <RemixServer context={remixContext} url={request.url} />
    )
  );
  const styles = sheet.getStyleTags();
  markup = markup.replace("__STYLES__", styles);
  responseHeaders.set("Content-Type", "text/html");

  return new Response("<!DOCTYPE html>" + markup, {
    status: responseStatusCode,
    headers: responseHeaders,
  });
}

与该示例最大的区别似乎是,而不是使用hydrate, I use hydrateRoot正如我们对 React 18 所做的那样。不确定它是否与问题有任何关系:

// app/entry.client.tsx

import { RemixBrowser } from "@remix-run/react";
import { hydrateRoot } from "react-dom/client";

hydrateRoot(document, <RemixBrowser />);

The 有关 CSS-in-JS 库的 Remix 文档 https://remix.run/docs/en/v1/guides/styling#css-in-js-libraries说:“使用样式组件时,您可能会遇到水合警告。希望如此这个问题 https://github.com/styled-components/styled-components/issues/3660很快就会修复。”问题还没有解决,所以也许这个问题还没有解决方案。

但如果示例存储库有效,那么也许我错过了一些东西?


是的,渲染时间尤其是问题,因为服务器时间采用 UTC 格式,而客户端时间则是区域设置时间(UTC + X 小时)。这会导致两者的 UI 不同。

检查这一点的一种快速方法是在运行应用程序并尝试其页面之前将当前 CLI 实例的时区设置为 UTC:

export TZ=UTC

npm run dev

我们将看到 CSS 出现问题,如上面的问题所述。

有多种方法可以解决此问题,具体针对不同的用例。一是不发送日期时间对象。相反,将其作为字符串发送。例如:

const now: Date = new Date()

// Locale time as example only, we need to know client's locale time
const time: string = now.toLocaleTimeString([], {
               hour: "2-digit",
               minute: "2-digit",
             })

// Send time string to client.

这假设我们已经知道客户端的时区,因此我们可以使用它来设置/格式化服务器上​​的时间。

更灵活的方法是仅在页面装入后设置时间。例如:

const [now, setNow] = useState<Date>();
const loaderData = useLoaderData<string>();

useEffect(() => {
  if (!loaderData) {
    return;
  }

  setNow(JSON.parse(loaderData));
}, [loaderData]);

return <>{now.toLocaleTimeString([], {
            hour: "2-digit",
            minute: "2-digit",
          })}</>

使用这个解决方案,我们失去了 SSR 的一些好处。这有一定的影响。例如,我们需要特别注意 SEO(查看页面源代码,我们不会看到正确呈现的日期)。如果我们不这样做,机器人将无法正确索引应用程序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

重新混合水合作用失败:服务器和客户端上的 UI 不匹配 的相关文章

随机推荐