超全模态模型 × Harness 升级,升级解锁 ArkClaw,最新支持 DeepSeek-V4 系列与 GLM-5.1
我基于Next.js 14、TypeScript、Tailwind、Sanity和ShadCN构建的项目动态页面,用于展示Sanity上传的作品。点击按钮可触发ShadCN的Sheet组件显示Sanity数据,功能正常运行,但浏览器出现3条严重的水化失败错误,提示「服务器渲染的初始UI与客户端不匹配」,具体为「期望服务器HTML包含匹配的在内」。尝试过给按钮添加noHydratation属性,但认为这并非合理解决方案,需要排查问题根源。错误信息 #Unhandled Runtime Error Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <button> in <button>. Component Stack button button eval ./node_modules/@radix-ui/react-primitive/dist/index.mjs (39:26) eval ./node_modules/@radix-ui/react-dialog/dist/index.mjs (90:28) Provider ./node_modules/@radix-ui/react-context/dist/index.mjs (45:28) $5d3850c4d0b4e6c7$export$3ddf2d174ce01153 ./node_modules/@radix-ui/react-dialog/dist/index.mjs (60:28) div Call Stack throwOnHydrationMismatch node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7088:8) throwOnHydrationMismatch node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7117:6) tryToClaimNextHydratableInstance node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (16524:4) updateHostComponent$1 node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (18427:13) apply node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20498:13) dispatchEvent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20547:15) apply node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20622:28) invokeGuardedCallback node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (26813:6) beginWork node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25637:11) performUnitOfWork node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25623:4) workLoopConcurrent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25579:8) renderRootConcurrent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24432:37) callback node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (256:33) workLoop node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (225:13) flushWork node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (534:20) 原代码 #import ImageGallery from "@/app/components/ImageGallery" import { client } from "../../../utils/sanity/client" // UI SHADCN import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import Link from "next/link" // SANITY DATA async function getData(slug: string) { const query = `*[_type == 'job' && slug.current == "${slug}"][0]{ _id, images, name, description, objetive, releaseDate, link, "slug": slug.current, "categoryName": category->name, }` const data = client.fetch(query) return data } export default async function JobUI({ params } : { params: {slug: string} }) { const data: job = await getData(params.slug) return( <div className="flex gap-3"> <ImageGallery images={data.images}/> <Sheet > <SheetTrigger><Button>Ver Información</Button></SheetTrigger> <SheetContent className="w-96"> <SheetHeader> <SheetTitle className="mt-5"> <p>{data.name}</p> <p className="text-orange-500">Categoria: {data.categoryName}</p> </SheetTitle> <SheetDescription> <p className="text-sm font-bold">Descripción</p> <p>{data.description}</p> </SheetDescription> <SheetDescription> <p className="text-sm font-bold">Objetivo</p> <p>{data.objetive}</p> </SheetDescription> <Link href={data.link} target="_blank"><Button>Link del Proyecto</Button></Link> </SheetHeader> </SheetContent> </Sheet> </div> ) } 解决方案 #问题根源 #ShadCN的SheetTrigger组件本身会渲染一个<button>元素,而你在SheetTrigger内部又嵌套了ShadCN的Button组件,导致HTML结构变成了<button><button>...</button></button>——这种嵌套按钮的写法不符合HTML规范,同时直接引发了Next.js的水化不匹配错误。修复方案 #方案1:给SheetTrigger添加Button样式,不嵌套Button组件直接将按钮文本放在SheetTrigger内部,同时把ShadCN Button的样式类应用到SheetTrigger上,避免嵌套:<SheetTrigger className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2"> Ver Información </SheetTrigger> 方案2:使用asChild属性让SheetTrigger接受Button作为子元素如果你的ShadCN版本支持asChild属性(这是ShadCN组件的标准特性),可以直接将Button作为SheetTrigger的子元素传递,避免嵌套按钮:<SheetTrigger asChild> <Button>Ver Información</Button> </SheetTrigger> 如果你的SheetTrigger组件没有asChild属性,需要修改@/components/ui/sheet.tsx文件,参考ShadCN官方Sheet组件的实现添加该属性支持。额外检查点 #确认Sanity返回的data在服务器和客户端完全一致,比如data.name、data.categoryName等字段不会在客户端渲染时出现undefined或不同的值检查ImageGallery组件是否存在水化问题,比如是否使用了仅客户端可用的API(如window)却没有做客户端渲染处理(比如用use client指令或dynamic导入)内容的提问来源于stack exchange,提问作者Ramiro Navarrete
noHydratation
Unhandled Runtime Error Error: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Expected server HTML to contain a matching <button> in <button>. Component Stack button button eval ./node_modules/@radix-ui/react-primitive/dist/index.mjs (39:26) eval ./node_modules/@radix-ui/react-dialog/dist/index.mjs (90:28) Provider ./node_modules/@radix-ui/react-context/dist/index.mjs (45:28) $5d3850c4d0b4e6c7$export$3ddf2d174ce01153 ./node_modules/@radix-ui/react-dialog/dist/index.mjs (60:28) div Call Stack throwOnHydrationMismatch node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7088:8) throwOnHydrationMismatch node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (7117:6) tryToClaimNextHydratableInstance node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (16524:4) updateHostComponent$1 node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (18427:13) apply node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20498:13) dispatchEvent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20547:15) apply node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (20622:28) invokeGuardedCallback node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (26813:6) beginWork node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25637:11) performUnitOfWork node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25623:4) workLoopConcurrent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (25579:8) renderRootConcurrent node_modules\next\dist\compiled\react-dom\cjs\react-dom.development.js (24432:37) callback node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (256:33) workLoop node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (225:13) flushWork node_modules\next\dist\compiled\scheduler\cjs\scheduler.development.js (534:20)
import ImageGallery from "@/app/components/ImageGallery" import { client } from "../../../utils/sanity/client" // UI SHADCN import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import Link from "next/link" // SANITY DATA async function getData(slug: string) { const query = `*[_type == 'job' && slug.current == "${slug}"][0]{ _id, images, name, description, objetive, releaseDate, link, "slug": slug.current, "categoryName": category->name, }` const data = client.fetch(query) return data } export default async function JobUI({ params } : { params: {slug: string} }) { const data: job = await getData(params.slug) return( <div className="flex gap-3"> <ImageGallery images={data.images}/> <Sheet > <SheetTrigger><Button>Ver Información</Button></SheetTrigger> <SheetContent className="w-96"> <SheetHeader> <SheetTitle className="mt-5"> <p>{data.name}</p> <p className="text-orange-500">Categoria: {data.categoryName}</p> </SheetTitle> <SheetDescription> <p className="text-sm font-bold">Descripción</p> <p>{data.description}</p> </SheetDescription> <SheetDescription> <p className="text-sm font-bold">Objetivo</p> <p>{data.objetive}</p> </SheetDescription> <Link href={data.link} target="_blank"><Button>Link del Proyecto</Button></Link> </SheetHeader> </SheetContent> </Sheet> </div> ) }
ShadCN的SheetTrigger组件本身会渲染一个<button>元素,而你在SheetTrigger内部又嵌套了ShadCN的Button组件,导致HTML结构变成了<button><button>...</button></button>——这种嵌套按钮的写法不符合HTML规范,同时直接引发了Next.js的水化不匹配错误。
SheetTrigger
<button>
Button
<button><button>...</button></button>
直接将按钮文本放在SheetTrigger内部,同时把ShadCN Button的样式类应用到SheetTrigger上,避免嵌套:
<SheetTrigger className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2"> Ver Información </SheetTrigger>
如果你的ShadCN版本支持asChild属性(这是ShadCN组件的标准特性),可以直接将Button作为SheetTrigger的子元素传递,避免嵌套按钮:
asChild
<SheetTrigger asChild> <Button>Ver Información</Button> </SheetTrigger>
如果你的SheetTrigger组件没有asChild属性,需要修改@/components/ui/sheet.tsx文件,参考ShadCN官方Sheet组件的实现添加该属性支持。
@/components/ui/sheet.tsx
data
data.name
data.categoryName
undefined
ImageGallery
window
use client
dynamic
内容的提问来源于stack exchange,提问作者Ramiro Navarrete
超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起
模型再升级,30秒超长叙事, 模态参考扩容
模型自由,工具不限,最新支持 Deepseek-V4 系列、GLM-5.3 系列
超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列
大模型19元起,Al应用9.9元畅享,新人首购爆款尽享优惠