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

如何阻止useState/useEffect在初始加载时修改预渲染HTML

问题原因与解决方案

你遇到的核心问题不是useEffect的执行逻辑,而是组件初始state与预渲染DOM不匹配:

你的todos初始值设为null,当React通过hydrateRoot挂载组件时,会对比组件的render输出与现有预渲染DOM。此时组件因为todos是null,不会渲染任何todo项,React会直接清除页面中预渲染好的todo节点——这和useEffect是否执行完全无关。


解决步骤

  1. 让初始state与预渲染数据对齐
    预渲染阶段已经生成了todos数据,你需要把这些数据作为todos的初始state。可以在SSG时将数据注入全局变量(比如window.__PRE_RENDERED_TODOS__),或者直接在组件中传入预渲染数据。

  2. 修正useEffect的依赖项
    你之前写的useEffect依赖是true,这是无效语法。React的依赖数组需要传入state、props或其他会变化的变量,这里应该依赖控制是否刷新的keep_pre_rendered_todos。

  3. 优化渲染逻辑
    用可选链?.替代todos &&的判断,语法更简洁,同时确保初始渲染时能匹配预渲染DOM。


修改后的代码示例

假设SSG阶段已将预渲染的todos存入全局变量window.__PRE_RENDERED_TODOS__:

// 从预渲染全局变量获取初始数据
const initialTodos = window.__PRE_RENDERED_TODOS__ || null;
const [keepPreRenderedTodos, updatePreRenderedTodos] = useState(true);
const [todos, setData] = useState(initialTodos);

useEffect(() => {
  if(keepPreRenderedTodos) return;
  
  fetch("https://jsonplaceholder.typicode.com/todos")
    .then((response) => response.json())
    .then((fetchedTodos) => setData(fetchedTodos))
    .catch((error) => {
      alert(`${error} 发生错误!`)
    });

}, [keepPreRenderedTodos]); // 修正为合法的依赖项

return (
  <div className="todos">
    {
      todos?.map((task) =>
        <div className="todo" key={task.id}>
          {task.title}
        </div>
      )
    }
  </div>
);

关键说明

  • 初始state使用预渲染数据,确保组件第一次render的输出与现有DOM完全匹配,React不会清除预渲染内容。
  • 只有当你主动将keepPreRenderedTodos设为false时,才会触发fetch请求更新todos。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 17:35:23