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

React组件点击Button two后重渲染但useEffect不执行的原因咨询

React useState与useEffect行为疑问解答

组件代码

function Test() {
    const [data, setData] = useState<Array<string>>([]);
    console.log('Hello:', data);
    
    useEffect(() => {
       console.log('data: ', data)
    }, [data])
    const test1 = () => {
        setData(data.concat('a'));
    }
    const test2 = () => {
        setData(data);
    }
    return (
        <>
           <button onClick={test1}>Button one</button>
           <button onClick={test2}>Button two</button>
       </>
    );
}

问题现象

点击Button one时行为符合预期:组件重新渲染,useEffect也会执行。但Button two的行为存在两个疑问:

  • 点击Button one后立即点击Button two,组件会重新渲染,但useEffect不执行。React通过Object.is比较来决定重渲染/执行effect,为何useState和useEffect的比较结果存在差异?既然组件决定重渲染,说明state值data已改变,但setData(data)操作怎么会让data发生变化?而不执行effect又说明data依赖未变化,这两个决定显然矛盾。
  • 点击Button one后第二次点击Button two,组件无任何反应,这完全符合预期。

原因解释

这事儿核心是React的更新逻辑、闭包捕获值,以及开发环境严格模式的特殊行为共同导致的,拆解来看:

第一次点击Button two的现象解析

  1. 闭包捕获的state值:点击Button one后,组件重新渲染,此时test2函数已经捕获了当前的data值——也就是concat生成的新数组['a']。
  2. React的state对比逻辑:点击Button two调用setData(data)时,React会用Object.is对比传入的新值和内部维护的当前state。因为两者是同一个数组引用,所以判定为相等,正常情况下不会触发组件重渲染。
  3. “重渲染”的真相:你看到的组件重渲染,是开发环境下React严格模式的特殊行为——严格模式会在开发阶段重复调用组件的render函数,用来检测是否存在不纯的渲染逻辑,但这并不是状态更新触发的真正重渲染,生产环境下不会出现这个现象。
  4. useEffect不执行的原因:useEffect的依赖数组对比同样使用Object.is,由于新旧data是同一个数组引用,判定依赖没有变化,所以不会触发effect重新执行。

第二次点击Button two无反应的原因

第一次点击Button two时,React已经记录了这次更新的新旧值相等,后续再调用相同的setData(data)时,会直接跳过整个更新流程,自然不会有任何渲染或effect执行的行为。

额外补充:React中只有当setState传入的新值与旧值通过Object.is对比不相等时,才会触发生产环境下的真正组件重渲染。你这里的setData(data)传入的是和当前state完全相同的引用,所以正常情况下不会触发重渲染,开发环境的严格模式只是个特例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:25:47