英文:
React useState(), what is the correct way to wait for multiple setState() calls?
问题
在一个功能性的React组件内部,您可能需要更新多个状态变量。如何确保在运行下一个函数之前所有状态更改都已完成呢?
例如,假设在某个事件上触发calculateScore()
(也许是游戏中的某些点击),它会增加a、b或两者的值。然后,您希望运行determineWinner()
。如何以一种方式运行determineWinner()
,以确保setA
和setB
都已完成它们的异步操作,以便a和b都是最新的(在单个calculateScore()
运行后)?
我没有在文档中找到这个信息,也没有在StackOverflow上搜索一段时间后找到它。
示例:
function MyComponent() {
let [a, setA] = useState(0);
let [b, setB] = useState(0);
let [winnerText, setWinnerText] = useState("")
function determineWinner() {
if (a > b) {
// 请注意,如果得分在calculateScore()的setA和setB完成后相等,我不希望这种情况发生。
setWinnerText("A赢了!")
}
}
function calculateScore(aShouldIncrease, bShouldIncrease) {
if (aShouldIncrease) setA(prevA => prevA + 1);
if (bShouldIncrease) setB(prevB => prevB + 1);
determineWinner(); // 在这里运行它是不行的,因为setA和setB是异步的。
}
useEffect(() => {
determineWinner(); // 不能保证在这里工作,因为也许只有setA已经完成,而setB尚未完成?对吗?
}, [a, b])
determineWinner(); // 即使我们假设我有某种条件来防止它过早运行,它也可能在只有a或只有b已更改时运行,对吗?
return <h1>{ winnerText }</h1>
}
我意识到我可以通过在calculateScore
中进行所有计算而不设置状态来解决这个问题,但我想知道是否有一种在React中可以确保从单个函数调用中启动的所有setState()
函数都已完成的时机。
英文:
In a function inside a functional react component I may update multiple state variables. How can I ensure all state changes have taken place before running the next function.
Below, for example, imagine that calculateScore() is triggered on some event (perhaps some clicks in a game) and adds to the value of either a, b or both. I then wan't to run determineWinner(). How can I run determineWinner() in a way which ensures both setA and setB have completed their asynchronous runs so that both a and b are up-to-date (after a single calculateScore() run)?
I didn't find this in the docs or while searching around for a while here on stackOverflow.
Example
function MyComponent() {
let [a, setA] = useState(0);
let [b, setB] = useState(0);
let [winnerText, setWinnerText] = useState("")
function determineWinner() {
if (a > b) {
// Note, I don't want this to happen if the score ends up equal after both setA and setB from calculateScore() completes.
setWinnerText("A won!")
}
}
function calculateScore(aShouldIncrease, bShouldIncrease) {
if (aShouldIncrease) setA(prevA => prevA + 1);
if (bShouldIncrease) setB(prevB => prevB + 1);
determineWinner(); // Doesn't work to run it here, since setA and setB are asynchronous.
}
useEffect(() => {
determineWinner(); // Not guaranteed to work here, since perhaps only setA has completed so far and not setB? Right?
}, [a, b])
determineWinner(); // Even if we imagined I had some other condition to prevent it from being run prematurely, it may run when only a or only b has changed, right?
return <h1>{ winnerText }</h1>
}
I realize I could solve it by making all the calculations within the calculateScore without setting state, but I wonder if there is some point in react where I can be sure all setState() functions started from within a single function call will be completed.
答案1
得分: 2
从语义上看,似乎您想在“游戏得分”更新时执行一个操作,而不是在其中的任何单个值更新时执行操作。因此,当前的代码中没有考虑封装级别。
将“游戏状态”保留在一个状态对象中,而不是两个。例如,考虑以下方式:
const [scores, setScores] = useState({a: 0, b: 0});
然后,更新该状态将成为单一的“set”操作:
function calculateScore(aShouldIncrease, bShouldIncrease) {
const newScores = { ...scores };
if (aShouldIncrease) {
newScores.a++;
}
if (bShouldIncrease) {
newScores.b++;
}
if (aShouldIncrease || bShouldIncrease) {
setScores(newScores);
}
}
然后,由于只更新了一个状态对象,您可以仅触发具有该依赖关系的效果:
useEffect(() => {
determineWinner();
}, [scores]);
英文:
Semantically it sounds like you want to perform an action when "the game score" is updated, not when any individual value therein is updated. So there's a level of encapsulation not accounted for in the current code.
Keep the "game state" in one state object rather than two. For example, consider this:
const [scores, setScores] = useState({a: 0, b: 0});
Then updating that state would be a single "set" operation:
function calculateScore(aShouldIncrease, bShouldIncrease) {
const newScores = { ...scores };
if (aShouldIncrease) {
newScores.a++;
}
if (bShouldIncrease) {
newScores.b++;
}
if (aShouldIncrease || bShouldIncrease) {
setScores(newScores);
}
}
Then since only one state object was updated, you can trigger the effect with just that dependency:
useEffect(() => {
determineWinner();
}, [scores]);
答案2
得分: 0
function calculateScore(aShouldIncrease, bShouldIncrease) {
unstable_batchedUpdates(() => {
if (aShouldIncrease) setA(prevA => prevA + 1);
if (bShouldIncrease) setB(prevB => prevB + 1);
determineWinner();
});
}
英文:
function calculateScore(aShouldIncrease, bShouldIncrease) {
unstable_batchedUpdates(() => {
if (aShouldIncrease) setA(prevA => prevA + 1);
if (bShouldIncrease) setB(prevB => prevB + 1);
determineWinner();
});
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论