Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

水合错误 (Hydration Errors)

为什么会发生水合错误?


策略 1 —— 使服务器和客户端保持一致

// src/start.ts
import { createStart, createMiddleware } from "@tanstack/react-start";
import {
  getRequestHeader,
  getCookie,
  setCookie,
} from "@tanstack/react-start/server";

const localeTzMiddleware = createMiddleware().server(async ({ next }) => {
  const header = getRequestHeader("accept-language");
  const headerLocale = header?.split(",")[0] || "en-US";
  const cookieLocale = getCookie("locale");
  const cookieTz = getCookie("tz"); // 由客户端稍后设置(见策略 2)

  const locale = cookieLocale || headerLocale;
  const timeZone = cookieTz || "UTC"; // 在客户端发送时区之前保持确定性

  // 可选:为后续请求持久化语言区域
  setCookie("locale", locale, { path: "/", maxAge: 60 * 60 * 24 * 365 });

  return next({ context: { locale, timeZone } });
});

export const startInstance = createStart(() => ({
  requestMiddleware: [localeTzMiddleware],
}));
// src/routes/index.tsx (示例)
import * as React from "react";
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import { getCookie } from "@tanstack/react-start/server";

export const getServerNow = createServerFn().handler(async () => {
  const locale = getCookie("locale") || "en-US";
  const timeZone = getCookie("tz") || "UTC";
  return new Intl.DateTimeFormat(locale, {
    dateStyle: "medium",
    timeStyle: "short",
    timeZone,
  }).format(new Date());
});

export const Route = createFileRoute("/")({
  loader: () => getServerNow(),
  component: () => {
    const serverNow = Route.useLoaderData() as string;
    return <time dateTime={serverNow}>{serverNow}</time>;
  },
});

策略 2 —— 让客户端告知其环境信息

import * as React from "react";
import { ClientOnly } from "@tanstack/react-router";

function SetTimeZoneCookie() {
  React.useEffect(() => {
    const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
    document.cookie = `tz=${tz}; path=/; max-age=31536000`;
  }, []);
  return null;
}

export function AppBoot() {
  return (
    <ClientOnly fallback={null}>
      <SetTimeZoneCookie />
    </ClientOnly>
  );
}

策略 3 —— 采用纯客户端渲染 (Client-only)

import { ClientOnly } from "@tanstack/react-router";

<ClientOnly fallback={<span>—</span>}>
  <RelativeTime ts={someTs} />
</ClientOnly>;

策略 4 —— 为该路由禁用或限制 SSR

export const Route = createFileRoute("/unstable")({
  ssr: "data-only", // 或者设置为 false
  component: () => <ExpensiveViz />,
});

策略 5 —— 最后的手段:抑制警告

<time suppressHydrationWarning>{new Date().toLocaleString()}</time>

检查清单 (Checklist)

另请参阅:执行模型 (Execution Model)代码执行模式 (Code Execution Patterns)选择性 SSR (Selective SSR)服务器函数 (Server Functions)