You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js报错:文本内容与服务端渲染HTML不匹配的修复咨询

问题修复方案

错误原因分析

"Text content does not match server-rendered HTML" 错误根源是Next.js服务端渲染时生成的随机键盘数据,和客户端hydrate阶段生成的随机数据不一致。你把keyboard1、keyboard2定义在组件外部,服务端和客户端会各自执行这段代码生成不同的随机值,导致两边渲染的HTML内容不匹配。

src1、src2的作用说明

你代码里的src1、src2是冗余状态——定义了但在return里完全没用到(Image组件直接用"/" + keyboard1.name + ".jpg")。你觉得移除后功能异常,大概率是因为没同时把keyboard1、keyboard2改成状态管理,导致组件无法追踪数据变化重新渲染。

完整修复代码

import Head from 'next/head'
import Image from 'next/image'
import Link from 'next/link'
import { Inter } from '@next/font/google'
import React, { useState, useEffect } from 'react'

const inter = Inter({ subsets: ['latin'] })
const initialKeebs = [
  {name: "Hineybeige", votes: 0, id: 0},
  {name: "Vamp", votes: 0, id: 1},
  {name: "Fright Club Volume 1", votes: 0, id: 2},
  {name: "Tenshi", votes: 0, id: 3},
  {name: "Lilac on Black", votes: 0, id: 4},
  {name: "Handarbeige", votes: 0, id: 5},
  {name: "Darling", votes: 0, id: 6},
  {name:"Olivia++", votes: 0, id: 7}
]

// 生成两个不重复的随机键盘
const getRandomKeebPair = (keebs: typeof initialKeebs) => {
  let k1 = keebs[Math.floor(Math.random() * keebs.length)]
  let k2 = keebs[Math.floor(Math.random() * keebs.length)]
  while (k1.name === k2.name) {
    k2 = keebs[Math.floor(Math.random() * keebs.length)]
  }
  return [k1, k2]
}

export default function Home() {
  // 用状态管理键盘数据和投票数,避免全局变量导致的SSR不匹配
  const [keebs, setKeebs] = useState(initialKeebs)
  const [keyboard1, setKeyboard1] = useState<typeof initialKeebs[0]>(() => getRandomKeebPair(initialKeebs)[0])
  const [keyboard2, setKeyboard2] = useState<typeof initialKeebs[0]>(() => getRandomKeebPair(initialKeebs)[1])

  // 初始化时确保两个键盘不重复
  useEffect(() => {
    const [k1, k2] = getRandomKeebPair(keebs)
    setKeyboard1(k1)
    setKeyboard2(k2)
  }, [])

  const handleSubmit = (voteFor: 1 | 2) => {
    // 更新投票数,通过setKeebs触发组件重新渲染
    setKeebs(prevKeebs => {
      return prevKeebs.map(keeb => {
        if (voteFor === 1 && keeb.id === keyboard1.id) {
          return {...keeb, votes: keeb.votes + 1}
        } else if (voteFor === 2 && keeb.id === keyboard2.id) {
          return {...keeb, votes: keeb.votes + 1}
        }
        return keeb
      })
    })

    // 生成新的随机键盘对
    const [newK1, newK2] = getRandomKeebPair(keebs)
    setKeyboard1(newK1)
    setKeyboard2(newK2)
  }

  return (
    <>
      <Head>
        <title>Keycap Vote</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main className="flex min-h-screen flex-col items-center bg-gradient-to-b from-[#2e026d] to-[#15162c]">
        <div className="container flex flex-col items-center gap-12 px-4 py-16 ">
          <h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[5rem]">
            Keycap <span className="text-[hsl(280,100%,70%)]">Vote</span> App
          </h1>
        </div>
        <Link href="/api/auth/login">Login</Link>

        <div className="grid grid-cols-2 gap-4 sm:grid-cols-2 md:gap-8">
          <h1 className="text-2xl font-extrabold tracking-tight text-white sm:text-[3rem] container flex flex-col items-center">
            {keyboard1.name}
          </h1>
          <h1 className="text-2xl font-extrabold tracking-tight text-white sm:text-[3rem] container flex flex-col items-center">
            {keyboard2.name}
          </h1>

          <button onClick={() => handleSubmit(1)} className="flex max-w-md flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20">
            <div className="relative aspect-video h-64 max-w-[18rem]">
              <Image 
                src={`/${keyboard1.name}.jpg`} 
                alt={keyboard1.name} 
                layout="fill" 
                className="rounded-xl" 
              />
            </div>
          </button>

          <button onClick={() => handleSubmit(2)} className="flex max-w-md flex-col gap-4 rounded-xl bg-white/10 p-4 text-white hover:bg-white/20">
            <div className="relative aspect-video h-64 max-w-[18rem]">
              <Image 
                src={`/${keyboard2.name}.jpg`} 
                alt={keyboard2.name} 
                layout="fill" 
                className="rounded-xl" 
              />
            </div>
          </button>

          <h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[3rem] container flex flex-col items-center">
            {keebs.find(k => k.id === keyboard1.id)?.votes || 0}
          </h1>
          <h1 className="text-5xl font-extrabold tracking-tight text-white sm:text-[3rem] container flex flex-col items-center">
            {keebs.find(k => k.id === keyboard2.id)?.votes || 0}
          </h1>
        </div>
      </main>
    </>
  )
};

关键修复点

  • 将keyboard1、keyboard2和keebs都改为组件内部的状态管理,避免全局变量导致的SSR数据不一致。
  • 用useState的初始化函数和useEffect生成初始随机键盘对,确保服务端和客户端初始渲染一致。
  • 更新投票数时使用setKeebs的回调函数,保证基于最新状态更新,触发组件重新渲染。
  • 移除了冗余的src1、src2状态,直接通过keyboard1、keyboard2的name生成图片路径。

内容的提问来源于stack exchange,提问作者Alex Lofnes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:43:04