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

useRef包裹的函数内访问React state不更新如何解决

问题产生原因
  • 核心原因是陈旧闭包+useRef的初始化特性:useRef 仅会在组件首次渲染时执行传入的初始化逻辑,后续组件重渲染时,ref.current 保存的值不会自动更新。你首次渲染时传给useRef的回调函数,闭包捕获的是首次渲染阶段的getCount方法,以及当时的初始state值count=0,后续重渲染时onClick.current始终指向第一次创建的旧函数,自然读不到更新后的state。
  • 你写的.bind(App)是完全无效的操作:函数组件不存在类组件那样的组件实例,把函数的this绑定到App组件函数本身,既访问不到组件内部的state,也不会自动同步最新值,对解决问题没有任何帮助。
  • React函数组件的渲染机制决定了:函数组件每次重渲染都会从头执行一遍整个函数体,每一轮渲染都有独立的state、变量、方法快照,如果你把某一轮渲染生成的函数存在ref里不更新,它永远只能访问对应轮次的快照值。
  • 额外的隐藏问题:你直接把onClick.current绑定给DOM的onClick事件,绑定动作发生在渲染阶段,拿到的也是当次渲染时的current值,就算后续更新current,已经绑定的事件处理器还是旧的。
修复方案

根据实际场景选择对应方案即可:

方案1:用ref同步最新state,适配需要固定回调引用的场景

如果场景要求事件处理器引用保持稳定(比如要传给做了memo优化的子组件避免重渲染),可以额外建一个ref存储最新的state值,每次渲染后把最新state同步到ref上,回调里直接读ref的最新值即可:

import React, { useState, useRef, useEffect } from 'react'
import ReactDOM from 'react-dom'

const App = function () {
  const [count, setCount] = useState(0)
  // 存储最新state的ref
  const latestCountRef = useRef(count)
  // 每次count变化后同步最新值到ref
  useEffect(() => {
    latestCountRef.current = count
  }, [count])

  console.log('render时count值:', count)

  // 回调初始化一次即可,永远读ref上的最新值
  const onClick = useRef(() => {
    console.log('事件内拿到的count:', latestCountRef.current)
  })

  return (
    <>
      <div onClick={() => setCount(prev => prev + 1)}>
        increment count
      </div>
      {/* 包一层箭头函数,触发时才读取最新的current回调 */}
      <div onClick={() => onClick.current()}> 
        {count}
      </div>
    </>
  )
}

const wrapper = document.getElementById('root')
ReactDOM.render(<App />, wrapper)

方案2:每次渲染更新ref上的回调

同样适用于需要保持ref引用稳定的场景,每次重渲染后把捕获了最新state的回调赋值给ref.current,触发事件时调用最新的回调即可:

import React, { useState, useRef, useEffect } from 'react'
import ReactDOM from 'react-dom'

const App = function () {
  const [count, setCount] = useState(0)
  const getCount = () => count
  console.log('render时count值:', getCount())

  const onClick = useRef(() => {})
  // 每次渲染后更新ref上的回调为最新版本
  useEffect(() => {
    onClick.current = () => {
      console.log('事件内拿到的count:', getCount())
    }
  })

  return (
    <>
      <div onClick={() => setCount(prev => prev + 1)}>
        increment count
      </div>
      <div onClick={() => onClick.current()}> 
        {count}
      </div>
    </>
  )
}

const wrapper = document.getElementById('root')
ReactDOM.render(<App />, wrapper)

方案3:无特殊性能需求直接用普通函数

如果没有强制要求回调引用保持稳定,完全没必要用useRef包裹事件回调,直接定义普通函数即可,React每次重渲染会自动更新DOM上绑定的事件处理器,天然能拿到最新的state:

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

const App = function () {
  const [count, setCount] = useState(0)
  const getCount = () => count
  console.log('render时count值:', getCount())

  // 普通回调,每次渲染生成最新版本
  const onClick = () => {
    console.log('事件内拿到的count:', getCount())
  }

  return (
    <>
      <div onClick={() => setCount(prev => prev + 1)}>
        increment count
      </div>
      <div onClick={onClick}> 
        {count}
      </div>
    </>
  )
}

const wrapper = document.getElementById('root')
ReactDOM.render(<App />, wrapper)

注意:更新state时推荐写成setCount(prev => prev + 1)的形式,避免闭包导致的state更新不及时问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:06:29