当前位置: 首页 > 知识库问答 >
问题:

React hooks:从回调中访问最新状态

仲阳朔
2023-03-14

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

--原问题在下面继续--

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

使用以下代码。无论我点击多少次,控制台都将继续打印计数为:0

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);

你可以在这里找到这个代码

我在回调中设置状态没有问题,只是在访问最新状态时。

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

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

具有

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

你可以在这里找到这个代码

这种方法也没有奏效。编辑:实际上,第二种方法确实有效。我刚才回电话时打错了。这是正确的做法。我需要调用setState来访问上一个状态。即使我无意设定国家。

我觉得我对React类采取了类似的方法,但是。为了代码的一致性,我需要坚持使用反应效果。

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

共有3个答案

巫坚白
2023-03-14

我遇到了一个类似的bug,它试图执行与您在示例中所做的完全相同的操作—在引用React组件的propsstate的回调上使用setInterval

希望我能从一个稍微不同的方向来解决这个问题,从而补充这里已经给出的好答案——认识到这甚至不是一个React问题,而是一个普通的老Javascript问题。

我认为这里吸引人的是React-hooks模型,其中状态变量,毕竟只是一个局部变量,可以被视为在React组件的上下文中是有状态的。您可以确定,在运行时,变量的值将始终是React在特定状态下保持的值。

然而,一旦您脱离了React组件上下文(例如,在setInterval内的函数中使用变量),抽象就中断了,您回到了这样一个事实:状态变量实际上只是一个持有值的局部变量。

抽象允许您编写代码,就好像运行时的值总是反映状态一样。在React的上下文中,情况就是这样,因为每当您设置状态时,整个函数都会再次运行,并且React将变量的值设置为更新后的状态值。然而,在回调中,没有这样的事情发生——该变量不会神奇地更新以反映调用时的基础React状态值。它就是定义回调时的样子(在本例中是0),并且永远不会改变。

这里是我们得到解决方案的地方:如果该局部变量指向的值实际上是对可变对象的引用,那么情况会发生变化。该值(即引用)在堆栈上保持不变,但它在堆上引用的可变值可以更改。

这就是为什么接受答案中的技术起作用的原因——React ref正好提供了对可变对象的引用。但我认为必须强调的是,这其中的“反应”部分只是巧合。解决方案和问题一样,与React本身无关,只是React ref恰好是获取对可变对象的引用的一种方法。

例如,您还可以使用一个普通Javascript类,将其引用保持在React状态。明确地说,我并不是说这是一个更好的解决方案,甚至不是一个可取的解决方案(可能不是!),而是用它来说明这个解决方案没有“反应”方面——它只是Javascript:

class Count {
  constructor (val) { this.val = val }
  get () { return this.val }
  
  update (val) {
    this.val += val
    return this
  }
}

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

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

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

您可以在那里看到,只需让状态指向一个引用,它不会改变,并改变引用指向的基础值,您就可以在setInterval闭包和React组件中获得您所追求的行为。

同样,这不是惯用的React,只是说明了引用是这里的最终问题。希望对你有帮助!

易成天
2023-03-14

使用NPM模块react-usestateref始终获取最新的状态值。它完全向下兼容ReactuseStateAPI。

如何使用它的示例代码:

import useState from 'react-usestateref';

const [count, setCount, counterRef] = useState(0);

console.log(couterRef.current); // it will always have the latest state value
setCount(20);
console.log(counterRef.current);

NPM包react useStateRef允许您使用useState访问最新状态(如ref)。

为了准确地解决这个问题,我为此创建了一个反应模块。react-usestateref(React useStateRef)。例如使用:

var [state, setState, ref] = useState(0);

它的工作原理非常类似于useState,但除此之外,它还提供了ref.current

了解更多:

  • https://www.npmjs.com/package/react-usestateref

您可以使用setState获取最新的值

例如:

var [state, setState] = useState(defaultValue);

useEffect(() => {
   var updatedState;
   setState(currentState => { // Do not change the state by getting the updated state
      updateState = currentState;
      return currentState;
   })
   alert(updateState); // the current state.
})
乌骏
2023-03-14

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

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>);

}

您的回调可以引用可变对象来“读取”当前状态。它将在其闭包中捕获可变对象,并且每个呈现可变对象都将用当前状态值更新。

 类似资料:
  • 我有一个组件反应本机: 最后一行是将在新位置调用的回调方法。 通过该方法,我无法访问或 如何从回调函数更新状态?

  • 希望你们今天过得愉快。我是第一次对原生动画做出反应,遇到了一个问题。问题是我正在更新动画回调中的状态,就像这样 状态已更新,但当第二次调用此动画函数时,它无法访问最新状态。如果activeIndex为0,并且第一次运行时,activeIndex更新为1,但第二次调用此动画函数时,activeIndex仍然为0,并且仍然更新为1,依此类推。activeIndex在此回调外部更新—这只发生在回调内部。

  • 问题内容: 是否有人在React 16.8中为useState挂钩的更新部分创建了同步回调?我一直在寻找一种方法,这样我就可以使用第三方库来处理同步操作,而我似乎无法根据自己的需要来完成一项工作。 如果有人对成功完成此操作的人员有任何引用,请在此处添加。 干杯, 问题答案: 使用钩子,您不再需要该函数的回调。现在,您可以使用挂钩设置状态,并监听其值以使用挂钩更新。挂钩的可选第二个参数采用一组值来侦

  • 问题内容: 我想使用React.js制作一个应用程序。我希望它可以轻松地从外部进行自定义(例如,通过编写用户脚本)。我尝试使用的想法是在根元素状态(如或)中创建一些特殊的属性,以便插件开发人员可以在此处添加一些内容。我的问题是:这是否是一个好方法,是否有Right Way™实现与我的目标相似的目标,最后,插件开发人员将如何使用这些道具? 问题答案: 一种选择是可观察的。基本上,它是一个对象,您可以

  • 我想在状态尚未装入时访问状态的属性。 我想做4,但得到以下错误: P. S也不起作用。

  • 问题内容: 我继承了具有以下结构的表: 是一个标识字段和主键,并且and字段上有非唯一索引。 什么是最有效的方式来获得每个项目名称的最新记录,即在上述行的表 1 , 4 和 6 ,因为他们是最先进的日期项物品应归还 一 , 乙 和 Ç 分别。 问题答案: SQL Server 2005(开始):