正如其他成员所提到的,setState运行在基于队列的异步系统中。这意味着如果你想进行状态更新,该更新将被堆积在队列中。然而,它不会返回Promise,因此你不能使用.then
或wait
。由于这个原因,我们需要注意在React中操作状态可能出现的问题。
比如说,我们有:
export function Button() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
console.log(count);
}
return (
<button onClick={increment}>Increment<\button>
)
}
这基本上是你所遇到的相同问题。由于setCount是异步的,
console.log
会在更新之前返回值(这里为0)。在你的情况下,它将返回
undefined
,因为你没有在useState钩子中传递任何初始状态。你所做的另一件不同的事情是从
useEffect
钩子内部触发状态变化,但这并不影响手头的问题。
解决方法是,一个好的实践是将新状态存储在另一个变量中,如下所示:
export function Button() {
const [count, setCount] = useState(0);
function increment() {
const newCountValue = count + 1;
setCount(newCountValue);
console.log(newCountValue);
}
return (
<button onClick={increment}>Increment<\button>
)
}
这基本上回答了你的问题。
但是,在其他情况下,状态更新可能不像人们期望的那样工作。了解这些场景以避免可能出现的错误非常重要。例如,假设我们连续多次更新给定状态,如下所示:
export function Button() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
console.log(count);
}
return (
<button onClick={increment}>Increment<\button>
)
}
在这里,console.log
仍然会因为问题#1描述的原因返回0。但是,如果您在应用程序中某个地方使用了count
状态,您将看到在onClick
事件之后的最终值为1而不是3。
以下是您可以修复此问题的方法:
export function Button() {
const [count, setCount] = useState(0);
function increment() {
setCount((oldState) => oldState + 1);
setCount((oldState) => oldState + 1);
setCount((oldState) => oldState + 1);
console.log(count);
}
return (
<button onClick={increment}>Increment<\button>
)
}
通过向setState传递一个函数,新状态是使用队列中的先前值而不是初始值计算出来的。
最后举个例子,假设我们在尝试改变状态之后立即调用一个函数,但这个函数依赖于状态值。由于操作是异步的,该函数将使用“旧”的状态值,就像问题#1中一样:
export function Button() {
const [count, setCount] = useState(0);
onCountChange() {
console.log(count);
}
function increment() {
setCount(count + 1);
onCountChange();
}
return (
<button onClick={increment}>Increment<\button>
)
}
注意在这个问题中,我们有一个依赖于外部变量(
count
)的函数
onCountChange
。所谓外部变量是指该变量没有在
onCountChange
作用域内声明。它们实际上是兄弟节点(它们都在
Button
作用域内)。现在,如果您熟悉“纯函数”、“幂等”和“副作用”概念,您会意识到我们面临的是一个副作用问题——我们希望在状态改变后发生某些事情。
粗略地说,每当您需要处理React中的一些副作用时,可以通过useEffect钩子来完成。因此,要解决我们的第三个问题,我们可以简单地执行如下操作:
export function Button() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
function increment() {
setCount(count + 1);
}
return (
<button onClick={increment}>Increment<\button>
)
}
在上述解决方案中,我们将
onCountChange
的内容移至 useEffect 中,并将
count
添加为依赖项,因此每当它发生更改时,都会触发该 effect。