React hooks:获取当前状态,返回未来

2025-06-10

React hooks:获取当前状态,返回未来

这篇文章虽然不是最近才写的,但仍然很有价值!而且我仍然经常看到开发者为此感到困扰。我在我的新电子书《React 开发者 Hooks 指南》中讨论了这个话题以及更多关于 React Hooks 的内容

React Hooks确实很棒,但是我玩得越多,就越发现其中的技巧,有时我会花很多时间来弄清楚为什么我的代码没有按照预期运行。

我的最后一个问题是:我想从异步触发的回调(例如在或中)访问useState组件的当前状态(用创建)useEffectuseCallback

下面是一个可能未按预期运行的代码示例:

const Counter = () => {
  const [counter, setCounter] = useState(0)
  const onButtonClick = useCallback(() => setCounter(counter + 1), [counter])

  const onAlertButtonClick = useCallback(
    () => {
      setTimeout(() => {
        alert('Value: ' + counter)
      }, 5000)
    },
    [counter]
  )

  return (
    <div>
      <p>You clicked {counter} times.</p>
      <button onClick={onButtonClick}>Click me</button>
      <button onClick={onAlertButtonClick}>
        Show me the value in 5 seconds
      </button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

你可能认出从React 文档中提取的计数器示例,我在其中添加了一个新按钮。点击此按钮后,五秒钟后会显示一个 alert,其中包含计数器的当前值。或者说,你可以想象,但不幸的是,显示的值并不是当前值。

假设您在计数器为 5 时点击了按钮,然后立即点击了三次递增按钮。您期望警报显示 8,但它显示的是 5。这是因为在传递给 的函数中setTimeoutcounter的值是 5,并且没有理由更新它(React Hooks 没有那么神奇)。这纯粹是 JavaScript 闭包和作用域的问题,所以显然我们需要找到另一种方法来实现我们想要的效果。

答案是:refs和 hooks useRef。我们的想法是使用 ref 作为计数器;每次 时它都会更新counter,然后我们会在传给 的函数中使用它的当前值setTimeout

因此,首先我们声明我们的 ref,并使用当前计数器值作为初始值:

const counterRef = useRef(counter)
Enter fullscreen mode Exit fullscreen mode

然后我们希望每次counter更新时都更新它,因此我们可以使用useEffect

useEffect(
  () => { counterRef.current = counter },
  [counter]
)
Enter fullscreen mode Exit fullscreen mode

最后,我们只需要使用counterRef.current超时函数:

const onAlertButtonClick = useCallback(() => {
  setTimeout(() => {
    alert('Value: ' + counterRef.current)
  }, 5000)
}, [])
Enter fullscreen mode Exit fullscreen mode

注意:我认为没有必要给出[counter]第二个参数,因为counterRef在渲染之间不应该改变。

效果非常好!我们甚至可以创建一个自定义钩子,使这个过程更简单、更可重用:

const useRefState = initialValue => {
  const [state, setState] = useState(initialValue)
  const stateRef = useRef(state)
  useEffect(
    () => { stateRef.current = state },
    [state]
  )
  return [state, stateRef, setState]
}
Enter fullscreen mode Exit fullscreen mode

我们的组件代码就变得简单多了:

const Counter = () => {
  const [counter, counterRef, setCounter] = useRefState(0)
  const onButtonClick = useCallback(() => setCounter(counter + 1), [counter])

  const onAlertButtonClick = useCallback(() => {
    setTimeout(() => {
      alert('Value: ' + counterRef.current)
    }, 5000)
  }, [])

  return (
    <div>
      <p>You clicked {counter} times.</p>
      <button onClick={onButtonClick}>Click me</button>
      <button onClick={onAlertButtonClick}>
        Show me the value in 5 seconds
      </button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

我不完全确定这是解决未来获取状态值问题的最佳方法,尽管它看起来运行良好。您在使用状态和钩子时遇到过同样的问题吗?您认为还有其他方法吗?或者这种方法有什么问题吗?

本文最初发布在我的博客上。照片由Sergey Zolkin 在 Unsplash 上拍摄。

鏂囩珷鏉ユ簮锛�https://dev.to/scastiel/react-hooks-get-the-current-state-back-to-the-future-3op2
PREV
架构是一种负担 简介 与整个公司共享数据库 围绕业务软件构建系统 数十个应用程序之间紧密耦合 在别人的项目上构建自己的项目 所有业务逻辑都在规则管理引擎中 结论
NEXT
在终端中调试 Python 代码(附示例)1. curl 2. Python 解释器