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

React 18开发模式下useRef在严格/非严格模式的行为差异

为什么React 18严格模式下useRef存prevCount会失效,而useState正常?

核心问题出在严格模式的双渲染对useRef和useState的影响逻辑完全不同,结合你的场景具体分析:

先看useRef方案的问题(导致trend始终为null)

假设你的useRef实现是这样的:

const prevCountRef = useRef(null);
const [count, setCount] = useState(0);

// 判断趋势
let trend = null;
if (prevCountRef.current !== null) {
  trend = count > prevCountRef.current ? 'increasing' : 'decreasing';
}

// 同步更新ref存当前count
prevCountRef.current = count;

React 18严格模式在开发环境会强制组件渲染两次,这两次渲染都会完整执行组件函数体的代码:

  1. 第一次渲染:prevCountRef.current初始为null,trend是null,然后把current设为当前count(比如初始的0)。
  2. 第二次渲染:此时count还是0,prevCountRef.current已经被第一次渲染改成0了,对比后trend还是null。

当你点击Increment按钮触发setCount(1)后,组件再次双渲染:

  1. 第一次渲染:count=1,prevCountRef.current=0,此时trend是increasing,但紧接着把current改成1。
  2. 第二次渲染:count=1,prevCountRef.current=1,对比后trend变回null。

React最终会采用第二次渲染的输出结果,所以你看到的trend始终是null。

再看useState方案为什么正常

如果用useState存prevCount,并且配合useEffect更新(这是正确的做法):

const [count, setCount] = useState(0);
const [prevCount, setPrevCount] = useState(null);

// 只有当count变化时,才更新prevCount
useEffect(() => {
  setPrevCount(count);
}, [count]);

// 判断趋势
let trend = null;
if (prevCount !== null) {
  trend = count > prevCount ? 'increasing' : 'decreasing';
}

原因在于:

  1. useEffect的执行时机是在渲染完成后,而且严格模式下的双渲染中,React会跳过第一次渲染的effect执行(模拟挂载后立即卸载,再执行第二次渲染的effect),最终prevCount只会被更新一次,保存的是上一轮的count值。
  2. useState的状态更新是由React调度管理的,不会像useRef那样在渲染同步修改被两次覆盖,所以prevCount和当前count始终能保持“上一次”和“当前”的差值,趋势判断自然正常。

总结

useRef的current属性是一个不受React状态管理的可变容器,在渲染过程中直接修改它,会被严格模式的双渲染干扰,导致“上一次值”被提前覆盖;而useState配合useEffect的方式,是遵循React状态更新规则的,严格模式的双渲染不会破坏状态的前后关系。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 16:03:36