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

React代码点击计数异常:三次点击输出2,2,3而非预期2,2,2求解析

问题分析与底层逻辑拆解

核心问题:普通变量无法在React渲染周期中保持状态

你定义的count是普通let变量,不是React的状态(useState)。React组件每次触发重渲染(比如调用setThings更新state时),整个App函数会重新执行一遍,这行let count = 1会把count重新初始化为1,根本存不住之前的累加值。

三次点击的具体执行流程

我们一步步理清楚每一次点击的运行逻辑:

第一次点击

  1. 初始渲染时,count = 1,state = 'pm'。
  2. 点击按钮触发updateThings:
    • count被改成1+1=2
    • 2是偶数,执行setThings((pre) => state = 'AM')(这里写法本身有问题,后面说)
    • 调用setThings会触发组件重渲染
    • 控制台打印2
  3. 组件重渲染,App函数重新跑一遍,let count = 1把count又重置回1,state被更新为'AM'。

第二次点击

  1. 当前渲染的count是重渲染后重置的1。
  2. 点击按钮触发updateThings:
    • count再次变成1+1=2
    • 执行setThings触发重渲染
    • 控制台打印2
  3. 组件重渲染,count又被重置为1,state切换为'PM'。

第三次点击(出现3的原因)

React 18在开发模式下会开启严格模式,它会对组件的更新函数执行两次调用,用来检测潜在的副作用问题:

  1. 当前count是1。
  2. 第一次执行updateThings:count变成2,触发setThings,但React的批量更新机制会暂存这次更新。
  3. 严格模式触发第二次执行updateThings:此时count已经是2(同一个更新周期里普通变量不会被重置),所以count = 2+1=3。
  4. 控制台打印3,之后组件重渲染,count又被重置为1。

这就是第三次点击输出3的根本原因——严格模式下的重复执行,加上普通变量没有状态持久化,导致count被累加了两次。

额外问题:setThings的写法完全错误

你写的setThings((pre) => state = 'AM')是典型的错误用法:

  • setThings的回调函数接收的pre是当前的前状态,正确写法应该是返回新状态,比如setThings(pre => 'AM')或者直接setThings('AM')。
  • 你直接给state变量赋值,绕开了React的状态更新机制,虽然表面可能有效果,但会导致状态不一致,因为React的state更新是异步且批量的,直接修改变量不会触发正确的重渲染逻辑。

修复方案

要解决问题,核心是把count改成React的状态变量,让它在渲染周期中持久化:

function App() {
  let [state, setThings] = React.useState('pm');
  // 把count改成React状态,初始值1
  let [count, setCount] = React.useState(1);
  let thing = '';

  function updateThings() {
    // 用setCount的回调,基于前状态计算新值,避免异步更新错误
    setCount(prevCount => {
      const newCount = prevCount + 1;
      // 根据新count值切换AM/PM
      setThings(newCount % 2 === 0 ? 'AM' : 'PM');
      console.log(newCount);
      return newCount;
    });
  }

  thing = <div>{state}</div>;
  
  return (
    <div id="main">
      <button
        type="button"
        onClick={updateThings}
        className="w-48 h-20 bg-green-400 rounded-2xl m-20 ring-2 ring-white"
      >
        click me
      </button>
      {thing}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

修复后逻辑:

  1. count作为React状态,每次更新都会被持久化,不会在重渲染时重置。
  2. 使用setCount的回调函数,基于前一个count值计算新值,避免异步更新导致的计数偏差。
  3. 正确调用setThings设置新状态,不再直接修改state变量。

此时三次点击控制台会输出2,3,4(符合点击次数的累加逻辑),同时AM/PM的切换也会正确对应count的奇偶性。


内容的提问来源于stack exchange,提问作者Mr. Mysterious

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:15:11