React useEffect+setTimeout渲染次数与W3School示例不符求助
React useEffect + setTimeout 渲染次数差异原因
核心原因:React 18 严格模式的副作用重复执行
你遇到的差异本质是React版本与严格模式的行为不同:
- W3School的示例运行在React 17或更早版本,或是未开启严格模式的环境,这类环境下组件挂载时
useEffect仅执行一次。 - 你的开发环境是React 18,默认开启严格模式,开发阶段会自动模拟「组件挂载→卸载→重新挂载」的流程,用于检测副作用稳定性,这会导致
useEffect被执行两次。
原代码的执行逻辑
假设你的原代码中useEffect的依赖数组包含count,执行流程如下:
- 初始渲染:
count初始值为0,页面显示「I've rendered 1 times」; - 第一次挂载时
useEffect执行,设置1秒后触发的setTimeout,回调函数捕获当前的count=0; - React 18严格模式触发组件卸载,随后重新挂载,
count重置为0,useEffect再次执行,又设置了一个setTimeout,同样捕获count=0; - 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
相关产品推荐
相关产品推荐

