React Hook useEffect疑问:为何索引1对应的Steve未渲染及打印?
为何索引值为1的Steve既未渲染到页面,也未在控制台打印?
我是一名React新手,正在学习网上的示例代码,遇到了问题:为何索引值为1的Steve既未渲染到页面,也未在控制台打印?
我的理解流程如下:
- 初始时状态变量index被初始化为0;
- 控制台打印NAMES数组长度为3;
- 组件渲染,页面显示Hello Mark!;
- 触发副作用useEffect,0 === (3-1)判断不成立;
- 调用setTimeout,2秒后将state变量index设为1;
- 但后续打印的index值为0;
- 2秒后,因依赖项index变化,再次调用useEffect,1 === 2判断不成立,再次调用setTimeout;
- 2秒后state变量index被设为2;
- 但我预期控制台会打印index为1,且Greet组件渲染Steve,请问我忽略了什么?
代码示例
App.js
import Greet from './Greet'; import { useEffect, useState } from 'react'; const NAMES = ["Mark", "Steve", "Glen"]; function App() { const[index, setIndex] = useState(0); console.log(`NAMES.Length : ${NAMES.length}`); useEffect(()=>{ if(index === NAMES.length - 1){ console.log(`index in condition : ${index}`); return; } setTimeout(()=> setIndex((index)=>index+1), 2000); console.log(`index after setTimeout : ${index}`); },[index]); return ( <div className="App"> <div className="content"> <Greet name={NAMES[index]} /> </div> </div> ); } export default App;
Greet.js
const Greet = ({name}) => { const message = `Hello, ${name}!`; console.log(message); return ( <div>{message}</div> ); } export default Greet;
控制台输出
控制台仅输出以下内容:
NAMES.Length : 3 Hello, Mark! index after setTimeout : 0 NAMES.Length : 3 Hello, Glen! index after setTimeout : 2 index in condition : 2
问题原因
核心问题出在React开发环境默认开启的严格模式上:
- 严格模式下,React会重复执行副作用函数(
useEffect)两次,用于检测潜在的副作用问题。 - 初始渲染时,
useEffect被执行两次,创建了两个完全相同的setTimeout定时器,都会在2秒后调用setIndex(prev => prev+1)。 - 2秒后,两个定时器同时触发:
- 第一个定时器将
index从0改为1; - 第二个定时器紧接着将
index从1改为2;
- 第一个定时器将
- React会批量处理这两次状态更新,直接将组件从
index=0的状态更新到index=2,跳过了index=1的中间渲染环节。 - 因此,
Greet组件从未渲染过Steve,控制台也不会打印对应的Hello, Steve!。
验证与解决
- 验证:关闭React严格模式(删除
index.js中包裹<App />的<React.StrictMode>标签),重新运行代码,你会看到Steve被正常渲染,控制台也会出现对应的打印日志。 - 解决:如果需要保留严格模式,可以在
useEffect中添加定时器清理逻辑,避免重复创建定时器:
加入清理函数后,每次useEffect(()=>{ if(index === NAMES.length - 1){ console.log(`index in condition : ${index}`); return; } const timer = setTimeout(()=> setIndex((index)=>index+1), 2000); console.log(`index after setTimeout : ${index}`); // 清理函数:组件卸载或依赖项变化时清除定时器 return () => clearTimeout(timer); },[index]);useEffect执行前都会清除上一次创建的定时器,避免多个定时器同时触发导致状态跳变。
内容的提问来源于stack exchange,提问作者InquisitiveLad
相关产品推荐
相关产品推荐

