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

React 18+特定场景下将ref.current作为useEffect依赖是否安全?

将myRef.current作为Effect依赖是否存在实际风险?

前提

  • 我知道通过const myref = useRef()定义的ref本身更新不会触发组件重渲染
  • 我也清楚有一条ESLint规则建议不要把myRef.current作为Effect依赖,也不要省略Effect里的依赖
  • 我了解可以用ref回调,但我用的ref是在Hook内部,完全不依赖DOM
  • 我考虑过用多个Effect,但我的场景里会出现竞态条件——它们都操作同一个状态,一个写入新状态,另一个读取的却是旧的未更新状态

用例说明

我用myRef来跟踪状态,不直接把state作为依赖,以此避免上述竞态条件,同时让外部调用setState时能自动触发这个Hook。

这个Hook的设计保证了:当value这类状态变化,或者外部调用setState(不是ref自身更新)时,Hook会重新运行。我已经在React v18里验证过,ref变化时第一个Effect会重新运行,目前没发现明显问题。

const [ state, setState ] = useState({})
const stateRef = useRef(state)
const [ value, setValue ] = useState(false)

const myHook = () => {
   useEffect(() => {
      if (!value) {
         // 计算并存储状态:setState(computedState),随后setValue(true)
      } else {
      // 在单独的渲染周期(即`value`变为true时),从stateRef.current读取状态(通过第二个Effect与`state`同步),并进行其他操作。此时(Effect第二次运行时)state尚未更新
      }
   }, [ value, stateRef.current] )

   useEffect(() => {
      stateRef.current = state // 修正原代码笔误:useRef → stateRef
   }, [state])

return [state, setState]
}

问题解答

把stateRef.current作为Effect依赖确实存在实际风险,核心原因如下:

  1. 依赖追踪的不可靠性:React的Effect依赖对比是在渲染阶段执行的,而ref.current可以在渲染之外的任意时机(比如Effect、事件回调)被修改。这意味着React无法精准追踪它的变化——即便你修改了stateRef.current,渲染阶段可能仍拿着旧值做对比,导致Effect不按预期触发。

  2. 版本兼容性隐患:你在React v18里测试正常,但这属于React的未定义行为。官方从未承诺过修改ref.current会触发Effect重新执行,未来版本的React可能会优化掉这种“意外触发”的逻辑,到时你的代码会直接失效。

  3. 代码可维护性差:依赖ref.current会让逻辑变得晦涩难懂,后续调试或接手的开发者很难理解依赖的合理性,一旦出现竞态或触发异常,排查成本会非常高。

替代方案

既然你的核心需求是避免竞态、同时能拿到最新state,可以调整逻辑,把状态同步和后续操作合并到同一Effect中,或者利用Effect的依赖特性直接使用state:

const [ state, setState ] = useState({})
const [ value, setValue ] = useState(false)

const myHook = () => {
   // 同步state并执行后续操作
   useEffect(() => {
      if (value) {
         // 这里直接用state即可,它是Effect的依赖,必然是最新值
         // 执行你需要的操作
      }
   }, [state, value])

   useEffect(() => {
      if (!value) {
         const computedState = /* 你的计算逻辑 */
         setState(computedState)
         setValue(true)
      }
   }, [value])

return [state, setState]
}

这种写法既规避了依赖ref.current的风险,又能保证逻辑的顺序性,不会出现读取旧状态的竞态问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:58:17