第二次调用 setState 会阻止第一次调用 setState 的执行
Second call to setState prevents first call to setState from executing
我正在制作剪刀石头布游戏。在下面的代码中,我有一个用于存储全局状态的上下文文件。我还展示了我的选择组件。当用户单击选择组件中的按钮时,将调用 setChoices 方法,该方法应在全局状态中设置用户选择和 cpu 选择变量。然后,cpuScore() 方法是 运行 之后增加 cpu 分数(只是为了说明问题)。 cpu 分数按预期更新,但选择变量未更新。如果我不使用 运行 cpuScore 方法,选择变量会按预期更新,但显然不是分数。
//上下文文件
import React, { createContext, useState } from 'react';
const GameContext = createContext();
const GameContextProvider = props => {
const [gameItems, setGameItems] = useState(
{userChoice: null, userScore: 0, cpuChoice: null, cpuScore: 0}
);
const setChoices = (userChoice, cpuChoice) => {
setGameItems({...gameItems, userChoice: userChoice, cpuChoice: cpuChoice})
}
const cpuScore = () => {
setGameItems({...gameItems, cpuScore: gameItems.cpuScore + 1})
}
return (
<GameContext.Provider value={{gameItems, setChoices, cpuScore}}>
{ props.children }
</GameContext.Provider>
)
}
export default GameContextProvider;
//选择组件
import React, { useContext } from 'react';
import { GameContext } from '../contexts/GameContext';
const Choices = (props) => {
const { setChoices, cpuScore } = useContext(GameContext);
const getCpuChoice = () => {
const cpuChoices = ['r', 'p', 's'];
const randomIndex = Math.floor((Math.random() * 3));
const cpuDecision = cpuChoices[randomIndex];
return cpuDecision
}
const playGame = (e) => {
const userChoice = e.target.id;
const cpuChoice = getCpuChoice();
setChoices(userChoice, cpuChoice);
cpuScore();
}
return (
<div>
<h1>Make Your Selection</h1>
<div className="choices">
<i className="choice fas fa-hand-paper fa-10x" id="p" onClick={playGame}></i>
<i className="choice fas fa-hand-rock fa-10x" id="r" onClick={playGame}></i>
<i className="choice fas fa-hand-scissors fa-10x" id='s' onClick={playGame}></i>
</div>
</div>
)
我需要更改什么来设置选项和分数的状态?
基本上,React 不会在您调用 setState
后立即更新。
如果您在 setChoices()
之后立即调用 cpuScore()
,您正在调用的 cpuScore
函数仍然是当前渲染中的函数,而不是在 setChoices()
更新之后。因此,cpuScore()
将再次设置状态,使用 gameItems
的传播值(它仍然没有改变,因为来自 setChoices 的更新还没有开始),这会导致 setChoices()
被覆盖。
如果 React 总是在每次 setState
调用后立即更新,性能会很糟糕。 React 所做的是它将多个 setState
调用批量化为一个更新,因此它不会重复更新 DOM。
const cpuScore = () => {
setGameItems({...gameItems, cpuScore: gameItems.cpuScore + 1})
}
我的建议是要么将这两个状态分开,这样它们就不会被彼此覆盖,要么创建一个新函数来在一个地方处理所有这些更新。
由于对 setState
的调用是异步的,您对 setState
的两次调用相互干扰,后一个会覆盖前一个。
你有几个选择。
- 分离您的状态,使值不会相互影响:
const [choices, setChoices] = useState({ user: null, cpu: null });
const [scores, setScores] = useState({ user: 0, cpu: 0);
或者更进一步,将两个选择和两个分数分别设置为自己的状态值。
- 将所有状态保存在一个对象中,但同时更新所有状态:
const setChoices = (userChoice, cpuChoice) => {
const cpuScore = gameItems.cpuScore + 1;
setGameItems({
...gameItems,
userChoice,
cpuChoice,
cpuScore
});
}
- 使用
useReducer
:
const initialState = {
userChoice: null,
userScore: 0,
cpuChoice: null,
cpuScore: 0
}
const [gameItems, dispatch] = useReducer((state, action) => {
switch (action.type) {
case "UPDATE_CHOICES":
return {
...state,
userChoice: action.userChoice,
cpuChoice: action.cpuChoice
};
case 'UPDATE_CPU_SCORE':
return {
...state,
cpuScore: state.cpuScore + 1
}
default:
return state;
}
}, initialState);
const setChoices = (userChoice, cpuChoice) => {
dispatch({ type: 'UPDATE_CHOICES', userChoice, cpuChoice });
};
const cpuScore = () => {
dispatch({ type: 'UPDATE_CPU_SCORE'})
};
我正在制作剪刀石头布游戏。在下面的代码中,我有一个用于存储全局状态的上下文文件。我还展示了我的选择组件。当用户单击选择组件中的按钮时,将调用 setChoices 方法,该方法应在全局状态中设置用户选择和 cpu 选择变量。然后,cpuScore() 方法是 运行 之后增加 cpu 分数(只是为了说明问题)。 cpu 分数按预期更新,但选择变量未更新。如果我不使用 运行 cpuScore 方法,选择变量会按预期更新,但显然不是分数。
//上下文文件
import React, { createContext, useState } from 'react';
const GameContext = createContext();
const GameContextProvider = props => {
const [gameItems, setGameItems] = useState(
{userChoice: null, userScore: 0, cpuChoice: null, cpuScore: 0}
);
const setChoices = (userChoice, cpuChoice) => {
setGameItems({...gameItems, userChoice: userChoice, cpuChoice: cpuChoice})
}
const cpuScore = () => {
setGameItems({...gameItems, cpuScore: gameItems.cpuScore + 1})
}
return (
<GameContext.Provider value={{gameItems, setChoices, cpuScore}}>
{ props.children }
</GameContext.Provider>
)
}
export default GameContextProvider;
//选择组件
import React, { useContext } from 'react';
import { GameContext } from '../contexts/GameContext';
const Choices = (props) => {
const { setChoices, cpuScore } = useContext(GameContext);
const getCpuChoice = () => {
const cpuChoices = ['r', 'p', 's'];
const randomIndex = Math.floor((Math.random() * 3));
const cpuDecision = cpuChoices[randomIndex];
return cpuDecision
}
const playGame = (e) => {
const userChoice = e.target.id;
const cpuChoice = getCpuChoice();
setChoices(userChoice, cpuChoice);
cpuScore();
}
return (
<div>
<h1>Make Your Selection</h1>
<div className="choices">
<i className="choice fas fa-hand-paper fa-10x" id="p" onClick={playGame}></i>
<i className="choice fas fa-hand-rock fa-10x" id="r" onClick={playGame}></i>
<i className="choice fas fa-hand-scissors fa-10x" id='s' onClick={playGame}></i>
</div>
</div>
)
我需要更改什么来设置选项和分数的状态?
基本上,React 不会在您调用 setState
后立即更新。
如果您在 setChoices()
之后立即调用 cpuScore()
,您正在调用的 cpuScore
函数仍然是当前渲染中的函数,而不是在 setChoices()
更新之后。因此,cpuScore()
将再次设置状态,使用 gameItems
的传播值(它仍然没有改变,因为来自 setChoices 的更新还没有开始),这会导致 setChoices()
被覆盖。
如果 React 总是在每次 setState
调用后立即更新,性能会很糟糕。 React 所做的是它将多个 setState
调用批量化为一个更新,因此它不会重复更新 DOM。
const cpuScore = () => {
setGameItems({...gameItems, cpuScore: gameItems.cpuScore + 1})
}
我的建议是要么将这两个状态分开,这样它们就不会被彼此覆盖,要么创建一个新函数来在一个地方处理所有这些更新。
由于对 setState
的调用是异步的,您对 setState
的两次调用相互干扰,后一个会覆盖前一个。
你有几个选择。
- 分离您的状态,使值不会相互影响:
const [choices, setChoices] = useState({ user: null, cpu: null });
const [scores, setScores] = useState({ user: 0, cpu: 0);
或者更进一步,将两个选择和两个分数分别设置为自己的状态值。
- 将所有状态保存在一个对象中,但同时更新所有状态:
const setChoices = (userChoice, cpuChoice) => {
const cpuScore = gameItems.cpuScore + 1;
setGameItems({
...gameItems,
userChoice,
cpuChoice,
cpuScore
});
}
- 使用
useReducer
:
const initialState = {
userChoice: null,
userScore: 0,
cpuChoice: null,
cpuScore: 0
}
const [gameItems, dispatch] = useReducer((state, action) => {
switch (action.type) {
case "UPDATE_CHOICES":
return {
...state,
userChoice: action.userChoice,
cpuChoice: action.cpuChoice
};
case 'UPDATE_CPU_SCORE':
return {
...state,
cpuScore: state.cpuScore + 1
}
default:
return state;
}
}, initialState);
const setChoices = (userChoice, cpuChoice) => {
dispatch({ type: 'UPDATE_CHOICES', userChoice, cpuChoice });
};
const cpuScore = () => {
dispatch({ type: 'UPDATE_CPU_SCORE'})
};