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
相关产品推荐
相关产品推荐

