React挂钩:从回调中访问最新状态 [英] React hooks: accessing up-to-date state from within a callback

查看:50
本文介绍了React挂钩:从回调中访问最新状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑(2020年6月22日):由于这个问题引起了人们的新兴趣,我意识到可能会有一些混乱的地方.因此,我想强调一下:问题中的示例旨在作为一个玩具示例.它不能反映问题.引发此问题的问题在于使用第三方库(在该库中控制有限),该库将回调作为函数的参数.向回调提供最新状态的正确方法是什么?在react类中,这可以通过使用 this 来完成.在React挂钩中,由于状态被封装在 React.useState()函数中的方式,如果回调通过 React.useState()获取状态,它将是过时的(设置回调时的值).但是,如果它设置状态,它将可以通过传递的参数访问最新状态.这意味着我们可以通过设置与以前相同的状态来使用React钩子在此类回调中获取最新状态.这行得通,但违反直觉.

EDIT (22 June 2020): as this question has some renewed interest, I realise there may be a few points of confusion. So I would like to highlight: the example in the question is intended as a toy example. It is not reflective of the problem. The problem which spurred this question, is in the use a third party library (over which there is limited control) that takes a callback as argument to a function. What is the correct way to provide that callback with the latest state. In react classes, this would be done through the use of this. In React hooks, due to the way state is encapsulated in the functions of React.useState(), if a callback gets the state through React.useState(), it will be stale (the value when the callback was setup). But if it sets the state, it will have access to the latest state through the passed argument. This means we can potentially get the latest state in such a callback with React hooks by setting the state to be the same as it was. This works, but is counter-intuitive.

-原始问题在下面继续-

-- Original question continues below --

我正在使用React钩子,并试图从回调中读取状态.每次回调访问它时,它都会恢复为默认值.

I am using React hooks and trying to read state from within a callback. Every time the callback accesses it, it's back at its default value.

使用以下代码.无论我单击多少次,控制台都将继续打印 Count is:0 .

With the following code. The console will keep printing Count is: 0 no matter how many times I click.

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  
  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
      setCallbackSetup(true)
    }
  }
  
  
  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);
  
}

const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);

您可以在此处

仅在访问最新状态时,在回调中设置状态就没有问题.

I've had no problem setting state within a callback, only in accessing the latest state.

如果我要猜测的话,我认为状态的任何改变都会创建Card函数的新实例.而且回调是指旧的回调.基于 https://reactjs.org/docs/hooks-reference上的文档.html#functional-updates ,我有一个想法,可以在回调中调用setState,然后将一个函数传递给setState,以查看是否可以从setState中访问当前状态.更换

If I was to take a guess, I'd think that any change of state creates a new instance of the Card function. And that the callback is referring to the old one. Based on the documentation at https://reactjs.org/docs/hooks-reference.html#functional-updates, I had an idea to take the approach of calling setState in the callback, and passing a function to setState, to see if I could access the current state from within setState. Replacing

setupConsoleCallback(() => {console.log(`Count is: ${count}`)})

使用

setupConsoleCallback(() => {setCount(prevCount => {console.log(`Count is: ${prevCount}`); return prevCount})})

您可以在此处

该方法也不起作用.实际上,第二种方法确实有效.我刚在回调中输入错误.这是正确的方法.我需要调用setState来访问以前的状态.即使我无意设置状态.

That approach hasn't worked either. Actually that second approach does work. I just had a typo in my callback. This is the correct approach. I need to call setState to access the previous state. Even though I have no intention of setting the state.

我觉得我对React类采取了类似的方法,但是.为了保持代码的一致性,我需要坚持使用React Effects.

I feel like I've taken similar approaches with React classes, but. For code consistency, I need to stick with React Effects.

如何从回调中访问最新的状态信息?

How can I access the latest state information from within a callback?

推荐答案

对于您的方案(无法继续创建新的回调并将其传递给第三方库的情况),可以使用 useRef 来使可变对象保持当前状态.像这样:

For your scenario (where you cannot keep creating new callbacks and passing them to your 3rd party library), you can use useRef to keep a mutable object with the current state. Like so:

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  const stateRef = useRef();

  // make stateRef always have the current count
  // your "fixed" callbacks can refer to this object whenever
  // they need the current value.  Note: the callbacks will not
  // be reactive - they will not re-run the instant state changes,
  // but they *will* see the current value whenever they do run
  stateRef.current = count;

  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${stateRef.current}`)})
      setCallbackSetup(true)
    }
  }


  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);

}

您的回调可以引用可变对象以读取"当前状态.它将捕获其闭包中的可变对象,并且每次渲染可变对象时都将使用当前状态值对其进行更新.

Your callback can refer to the mutable object to "read" the current state. It will capture the mutable object in its closure, and every render the mutable object will be updated with the current state value.

这篇关于React挂钩:从回调中访问最新状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆