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严格模式在开发环境会强制组件渲染两次,这两次渲染都会完整执行组件函数体的代码:
- 第一次渲染:
prevCountRef.current初始为null,trend是null,然后把current设为当前count(比如初始的0)。 - 第二次渲染:此时
count还是0,prevCountRef.current已经被第一次渲染改成0了,对比后trend还是null。
当你点击Increment按钮触发setCount(1)后,组件再次双渲染:
- 第一次渲染:
count=1,prevCountRef.current=0,此时trend是increasing,但紧接着把current改成1。 - 第二次渲染:
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'; }
原因在于:
useEffect的执行时机是在渲染完成后,而且严格模式下的双渲染中,React会跳过第一次渲染的effect执行(模拟挂载后立即卸载,再执行第二次渲染的effect),最终prevCount只会被更新一次,保存的是上一轮的count值。- useState的状态更新是由React调度管理的,不会像useRef那样在渲染同步修改被两次覆盖,所以
prevCount和当前count始终能保持“上一次”和“当前”的差值,趋势判断自然正常。
总结
useRef的current属性是一个不受React状态管理的可变容器,在渲染过程中直接修改它,会被严格模式的双渲染干扰,导致“上一次值”被提前覆盖;而useState配合useEffect的方式,是遵循React状态更新规则的,严格模式的双渲染不会破坏状态的前后关系。
内容的提问来源于stack exchange,提问作者shikher prasad
相关产品推荐
相关产品推荐

