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

React useEffect+setTimeout渲染次数与W3School示例不符求助

React useEffect + setTimeout 渲染次数差异原因

核心原因:React 18 严格模式的副作用重复执行

你遇到的差异本质是React版本与严格模式的行为不同:

  • W3School的示例运行在React 17或更早版本,或是未开启严格模式的环境,这类环境下组件挂载时useEffect仅执行一次。
  • 你的开发环境是React 18,默认开启严格模式,开发阶段会自动模拟「组件挂载→卸载→重新挂载」的流程,用于检测副作用稳定性,这会导致useEffect被执行两次。

原代码的执行逻辑

假设你的原代码中useEffect的依赖数组包含count,执行流程如下:

  1. 初始渲染:count初始值为0,页面显示「I've rendered 1 times」;
  2. 第一次挂载时useEffect执行,设置1秒后触发的setTimeout,回调函数捕获当前的count=0;
  3. React 18严格模式触发组件卸载,随后重新挂载,count重置为0,useEffect再次执行,又设置了一个setTimeout,同样捕获count=0;
  4. 1秒后两个setTimeout先后触发:
    • 第一个回调执行setCount(0+1=1),组件重新渲染,count变为1,页面更新为「I've rendered 2 times」;
    • 第二个回调执行setCount(0+1=1),但此时count已经是1,状态无变化,不会触发额外渲染。
      最终你看到的结果是2次渲染。

重构后代码的效果

当你将useEffect的依赖数组改为[](空数组),useEffect仅会在组件首次挂载阶段执行(即使在React 18严格模式下,两次执行的useEffect都捕获初始的count=0):

  • 若未添加清理函数,两次设置的定时器都会触发,但第一次触发后count变为1,第二次触发时状态无变化,不会更新渲染;
  • 若添加清理函数(return () => clearTimeout(timer)),第一次设置的定时器会在卸载时被清除,只会保留第二次的定时器,1秒后触发一次状态更新。
    你看到的「渲染1次」是初始渲染的结果,等待1秒后页面仍会更新为2次。

适配React 18严格模式的正确写法

如果想让定时器只触发一次,同时避免闭包问题,推荐使用函数式更新+清理副作用的写法:

import { useState, useEffect } from "react";

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timerId = setTimeout(() => {
      // 函数式更新,确保获取最新的状态值
      setCount(prev => prev + 1);
    }, 1000);
    // 清理定时器,避免内存泄漏和重复触发
    return () => clearTimeout(timerId);
  }, []);

  return <h1>I've rendered {count + 1} times</h1>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:40:38