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

Next.js 13.4 Hydration错误求助:服务端与客户端UI不匹配

Next.js 13.4 Hydration失败问题:服务端渲染UI与初始客户端UI不匹配

错误详情

未处理的运行时错误
错误:Hydration失败,因为初始UI与服务端渲染的内容不匹配。警告:预期服务端HTML的<ul>中包含对应的<li>。

组件栈

  • li
  • ul
  • div
  • Todos(路径:./src/components/Todos.tsx (15:118))
  • div

组件代码

"use client"

import {useTodos} from "@/store/todos";
import { useSearchParams } from "next/navigation";

export  const Todos = () => {
 const {todos, toggleTodoAsCompleted, handleDeleteTodo} = useTodos();

 const searchParams = useSearchParams();
 const todosFilter = searchParams.get('todos')
    console.log("params " + todosFilter)

    let filteredTodos = todos;

    if (todosFilter === "active") {
        filteredTodos = todos.filter((todo) => !todo.completed);
    } else if (todosFilter === "completed") {
        filteredTodos = todos.filter((todo) => todo.completed);
    }

    return (
        <div>
            <p>List of todos</p>
            
            <ul className="main-task">
                {filteredTodos.map((todo) => (
                    <li key={todo.id}>
                        <input
                            type="checkbox"
                            id={`todo-${todo.id}`}
                            checked={todo.completed}
                            onChange={() => {
                                console.log(todo.completed);
                                toggleTodoAsCompleted(todo.id);
                            }}
                        />
                        <label htmlFor={`todo-${todo.id}`}>{todo.task}</label>
                        {todo.completed && (
                            <button type="button" onClick={() => handleDeleteTodo(todo.id)}>
                                Delete
                            </button>
                        )}
                    </li>
                ))}
            </ul>
        </div>
    )
}

已尝试的解决方法

  • 将<ul>和<li>改为<div>和<section>
  • 查阅官方文档中Hydration错误的常见原因,但未解决问题

官方列出的Hydration错误常见原因

  • HTML标签嵌套错误
    • <p>嵌套在另一个<p>标签中
    • <div>嵌套在<p>标签中
    • 交互式内容无法嵌套(如<a>嵌套在<a>标签中、<button>嵌套在<button>标签中等)
  • 在渲染逻辑中使用typeof window !== 'undefined'这类检查
  • 在渲染逻辑中使用仅浏览器端可用的API,如window或localStorage
  • 浏览器扩展修改了HTML
  • CSS-in-JS库配置错误
    • 确保代码遵循官方示例
  • Edge/CDN配置错误,尝试修改HTML响应,如Cloudflare的自动压缩

解决方案

问题根源:useSearchParams在服务端渲染阶段无法获取到请求的查询参数,导致服务端渲染的列表内容与客户端hydrate时根据查询参数过滤后的内容不一致,触发Hydration不匹配。

修改代码,通过useEffect延迟处理查询参数,确保服务端先渲染完整列表,客户端完成hydrate后再更新过滤结果:

"use client"

import { useTodos } from "@/store/todos";
import { useSearchParams } from "next/navigation";
import { useEffect, useState } from "react";

export const Todos = () => {
  const { todos, toggleTodoAsCompleted, handleDeleteTodo } = useTodos();
  const searchParams = useSearchParams();
  const [filteredTodos, setFilteredTodos] = useState(todos);

  useEffect(() => {
    const todosFilter = searchParams.get('todos');
    console.log("params " + todosFilter);

    if (todosFilter === "active") {
      setFilteredTodos(todos.filter((todo) => !todo.completed));
    } else if (todosFilter === "completed") {
      setFilteredTodos(todos.filter((todo) => todo.completed));
    } else {
      setFilteredTodos(todos);
    }
  }, [searchParams, todos]);

  return (
    <div>
      <p>List of todos</p>
      
      <ul className="main-task">
        {filteredTodos.map((todo) => (
          <li key={todo.id}>
            <input
              type="checkbox"
              id={`todo-${todo.id}`}
              checked={todo.completed}
              onChange={() => {
                console.log(todo.completed);
                toggleTodoAsCompleted(todo.id);
              }}
            />
            <label htmlFor={`todo-${todo.id}`}>{todo.task}</label>
            {todo.completed && (
              <button type="button" onClick={() => handleDeleteTodo(todo.id)}>
                Delete
              </button>
            )}
          </li>
        ))}
      </ul>
    </div>
  )
}

说明

useEffect会在客户端组件完成hydrate后执行,此时useSearchParams能正确获取到查询参数,再根据参数更新过滤后的列表,保证服务端渲染的初始内容与客户端hydrate时的内容一致,解决Hydration不匹配问题。

内容的提问来源于stack exchange,提问作者Pranshu Sati

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 12:12:33