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.
——原问题继续如下——
我正在使用React钩子,并试图从回调中读取状态。每次回调访问它时,它都会回到默认值。
使用下面的代码。控制台会一直打印Count为: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}`)})
with
setupConsoleCallback(() => {setCount(prevCount => {console.log(`Count is: ${prevCount}`); return prevCount})})
你可以在这里找到这个代码
这种方法也没有奏效。
编辑:实际上第二种方法是可行的。我刚才回试的时候打错了。这是正确的做法。我需要调用setState来访问之前的状态。即使我无意设置状态。
我觉得我对React类采取了类似的方法,但是。为了代码的一致性,我需要坚持使用React Effects。
如何从回调中访问最新的状态信息?
不要尝试在回调中访问最近的状态,而是使用useEffect。使用setState返回的函数设置状态不会立即更新您的值。状态更新是批处理和更新的
如果您将useEffect()看作是setState的第二个参数(来自基于类的组件),这可能会有所帮助。
如果你想用最近的状态做一个操作,使用useEffect(),当状态改变时,它会被击中:
const {
useState,
useEffect
} = React;
function App() {
const [count, setCount] = useState(0);
const decrement = () => setCount(count-1);
const increment = () => setCount(count+1);
useEffect(() => {
console.log("useEffect", count);
}, [count]);
console.log("render", count);
return (
<div className="App">
<p>{count}</p>
<button onClick={decrement}>-</button>
<button onClick={increment}>+</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
更新
你可以为setInterval创建一个钩子,并像这样调用它:
const {
useState,
useEffect,
useRef
} = React;
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
function Card(title) {
const [count, setCount] = useState(0);
const callbackFunction = () => {
console.log(count);
};
useInterval(callbackFunction, 3000);
useEffect(()=>{
console.log('Count has been updated!');
}, [count]);
return (<div>
Active count {count} <br/>
<button onClick={()=>setCount(count+1)}>Increment</button>
</div>);
}
const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component'/>, el);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>
关于useEffect()的更多信息
2021年6月更新:
使用NPM模块react-usestateref总是获得最新的状态值。它完全向后兼容React useState API。
示例代码如何使用它:
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)。
2020年12月更新:
为了解决这个问题,我创建了一个react模块。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.
})