React 钩子:从回调中访问最新状态2021 年 6 月更新:2020年12月更新:原始答案

2022-08-30 02:00:45

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

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

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

使用以下代码。无论我单击多少次,控制台都会继续打印。Count is: 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 类上也采用了类似的方法,但是。为了保持代码的一致性,我需要坚持使用 React Effects。

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


答案 1

对于您的方案(您无法继续创建新回调并将其传递到第三方库),您可以使用来保留具有当前状态的可变对象。这样: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>);

}

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


答案 2

2021 年 6 月更新:

使用 NPM 模块始终获取最新的状态值。它与 React API 完全向后兼容。react-usestaterefuseState

如何使用它的示例代码:

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 允许您通过使用 访问最新状态(如 )。refuseState

2020年12月更新:

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

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

它的工作原理很好,但除此之外,它还为您提供了当前状态useStateref.current

了解更多信息:

原始答案

您可以使用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.
})