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

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开发环境默认开启的严格模式上:

  1. 严格模式下,React会重复执行副作用函数(useEffect)两次,用于检测潜在的副作用问题。
  2. 初始渲染时,useEffect被执行两次,创建了两个完全相同的setTimeout定时器,都会在2秒后调用setIndex(prev => prev+1)。
  3. 2秒后,两个定时器同时触发:
    • 第一个定时器将index从0改为1;
    • 第二个定时器紧接着将index从1改为2;
  4. React会批量处理这两次状态更新,直接将组件从index=0的状态更新到index=2,跳过了index=1的中间渲染环节。
  5. 因此,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 19:38:13