第二次调用setState阻止执行第一次调用setState

时间:2019-07-08 02:54:27

标签: reactjs react-hooks

我正在做石头,纸,剪刀的游戏。在下面的代码中,我有一个上下文文件用于存储全局状态。我还将展示我的选择部分。当用户单击选择组件中的按钮时,将调用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>
    )

我需要更改什么以设置选择和得分的状态?

2 个答案:

答案 0 :(得分:2)

由于对dll = CDLL('./test.so') func = dll.func func.argtypes = [c_int] func.restypes = POINTER(c_int*12) res = func(12)#Here res is an int res = cast(res, POINTER(c_int*12)).contents for i in res: print(i)#Segmentation fault: 11 的调用是异步的,因此您对setState的两个调用是相互干扰的,后一个将覆盖前一个。

您有一些选择。

  1. 分隔您的状态,以使值不会互相影响:
setState

或者走得更远,将两个选择和两个分数中的每一个设置为自己的状态值。

  1. 将所有状态保留在一个对象中,但立即更新所有状态:
const [choices, setChoices] = useState({ user: null, cpu: null });
const [scores, setScores] = useState({ user: 0, cpu: 0);
  1. 使用 const setChoices = (userChoice, cpuChoice) => { const cpuScore = gameItems.cpuScore + 1; setGameItems({ ...gameItems, userChoice, cpuChoice, cpuScore }); }
useReducer

答案 1 :(得分:0)

基本上,React不会在您调用setState之后立即更新。

如果在cpuScore()之后立即调用setChoices(),则您正在调用的cpuScore函数仍然是当前渲染中的函数,而不是在setChoices()更新之后。因此,cpuScore()将使用gameItems的传播值(由于尚未启动setChoices的更新而未更改)的传播值来再次设置状态,这会导致{ {1}}被覆盖。

如果React总是在每个setChoices()调用之后立即更新,那么性能将很糟糕。 React的作用是将多个setState调用分批进行一次更新,因此不会重复更新DOM。

setState

我的建议是将这两个状态分开,以免它们被彼此覆盖,或者创建一个新功能来一次处理所有这些更新。