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

React中useRef作为计数器点击按钮时自增两次问题

React 18中useRef计数器点击一次自增两次的问题解决

问题现象

在React 18开发环境下,使用useRef定义的计数器idRef,点击「Add name」按钮添加名称时,idRef.current会被自增两次,导致生成的id序列为1、3、5、7,而期望得到连续的1、2、3、4。

问题原因

React 18的Strict Mode在开发环境下会自动重复执行状态更新器函数、副作用函数等,目的是检测代码中可能存在的意外副作用。你的代码中直接在setNames的更新器里执行了idRef.current++——这个操作会直接修改ref的当前值,属于副作用操作,因此被Strict Mode重复执行了两次,导致id跳号。

解决方案

方案1:分离ref的读取与修改

先读取当前的id值,再单独执行自增操作,避免在状态更新器中直接修改ref:

const addName = () => {
  const currentId = idRef.current;
  idRef.current += 1;
  setNames((prevNames) => [
    ...prevNames,
    { id: currentId, name: inputRef.current.value },
  ]);
};

状态更新器中仅读取预先保存的currentId,即使更新器被重复执行,也不会再次修改ref的值,保证id只会自增一次。

方案2:使用useState管理计数器

遵循React的状态管理规范,用useState来维护计数器,而非useRef:

const { useRef, useState } = React;
function UseRefs() {
  const inputRef = useRef(null);

  const [idCounter, setIdCounter] = useState(1);
  const [names, setNames] = useState([]);

  const addName = () => {
    setNames((prevNames) => [
      ...prevNames,
      { id: idCounter, name: inputRef.current.value },
    ]);
    setIdCounter(prev => prev + 1);
  };

  // 其余代码不变
}

React会保证useState的更新逻辑即使在Strict Mode下被重复执行,最终状态也只会更新一次,不会出现跳号问题。

方案3:生产环境自动消除

Strict Mode的重复执行逻辑仅在开发环境生效,生产环境打包后不会出现该问题,但开发阶段仍建议修复,避免开发与生产行为不一致。

修改后的完整代码(方案1)

const { useRef, useState } = React;
function UseRefs() {
  const inputRef = useRef(null);

  const idRef = useRef(1);
  const [names, setNames] = useState([]);

  const addName = () => {
    const currentId = idRef.current;
    idRef.current++;
    setNames((prevNames) => [
      ...prevNames,
      { id: currentId, name: inputRef.current.value },
    ]);
  };

  return (
    <div>
      <div>
        {names.map((name) => (
          <div key={name.id}>
            {name.id}. {name.name}
          </div>
        ))}
      </div>
      <div className="m-3">
        <input type="text" ref={inputRef} />
        <button onClick={addName}> Add name </button>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.querySelector('.react')).render(<UseRefs />);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:46:09