繁体   English   中英

用 useState 钩子更新对象的 state 后做出反应,我不能调用它的任何函数

[英]React after updating an object's state with useState hook, I can't call any of its functions

我正在为上下文制作一个二十一点算牌培训应用程序。

它目前处理卡片并具有基本的hit功能,我在处理拆分(太多嵌套数组)时遇到了复杂性,所以我想我会制作一个PlayersPlayer对象,但我在尝试更新时遇到了问题state 与useState挂钩。

主要问题是我显然使用useState()钩子错误地更新了对象 state。 我幼稚的假设是,如果我有我的players器 object 及其内部 state,我调用函数来更新 state,然后做一个简单的setPlayers(players) 我在网上读到因为它是 object,所以你必须对其进行深度复制,以便 React 将其识别为新的 object 或类似的东西。 当我执行上述想法时,我的useEffect()钩子对players更新做出反应,从不执行 - 所以它没有接受 object 更改。 另一方面,我的用户界面确实更新了……这让我很困惑。

我尝试了许多在网上找到的不同方法(有很多关于此的帖子,但每个解决方案都给我同样的错误,让我觉得我只是不理解某些东西),但都取得了不同程度的成功。 如果我做

setPlayers({...players})

我的useEffect钩子的执行方式与setPlayers(player)不同。 问题在于这样做,在调用setPlayers({..players})之后,我在处理后尝试第二次击中时收到以下错误。 我可以处理,然后点击一次,然后我得到以下信息: Uncaught TypeError: players.hit is not a function 我使用回调找到了其他解决方案,但我得到了同样的错误。

我做了一个PlayersPlayer class(不是组件)来处理一些手部逻辑。 我遇到了处理拆分二十一点手的复杂性,所以我想我会将其抽象为 class。 我很确定将这些类与useState()一起使用会让我遇到问题。 我查看了有关更新 object 以便重新渲染的其他帖子,它们会产生不同的行为,但我真的不想要...

所以我的PlayersPlayer class 是非常有状态的,许多函数不返回值,他们只是更新他们的内部hands state。

这是我的PlayersPlayer class。

class Players {
    constructor(numPlayers) {
        this.numPlayers = numPlayers;
        this.players = new Array(numPlayers).fill().map( () => (new Player()));
        this.currentPlayer = 0;
    }
    hit(card) {
        this.players[this.currentPlayer].hit(card);
    }
}

class Player {
    constructor() {
        this.hands = [[]];
        this.currentHand = 0; // for splits
        this.numHands = 1;
        this.double = false;
    }
    getCurrentHand() {
        return this.hands[this.currentHand];
    }

    hit(card) {
        this.getCurrentHand().push(card);
        return this.hands;
      }
    //other functions like split() here, omitting for clarity.

这是我的应用程序启动的地方。 我让我的playerssetPlayers 所有这些都按预期工作,它初始化了它需要的 state,当一切准备就绪时,它会分发卡片。 我在屏幕上看到了我想要的卡片。 我将把我的Hands组件放在最后,所以这不是所有的浮动代码,但它与我不认为的问题无关。

export default function App() {

  const [deck, setDeck] = useState(initDeck());
  const [count, setCount] = useState(0);
  const [players, setPlayers] = useState(new Players(numPlayers));

  useEffect(() => {
    deal(deck, count, setCount, players, setPlayers);
  }, []);

  useEffect(() => {
    console.log(players);
  }, [players])
}

这就是我的问题所在:我经历了很多不同的方式,在 App 中,如何使用setPlayers() 我不会发布我所有的尝试,但所有结果要么是useEffect钩子没有在更改时执行,要么当我之前使用它时说某事不是 function。 它以某种方式错误地复制了 object 并失去了调用任何 function 的能力......

这是我在应用程序中的hit function,我正在处理我的const [players, setPlayers] = useState(new Players(numPlayers)); 我一开始就创造了。

我没有遇到setCount的任何问题,因为它只是一个简单的 integer。

function hit(deck, count, setCount, players, setPlayers) {
  let card = deck.pop();
  setCount(incrementCardCount(card, count));
  players.hit(card);
  setPlayers({...players}); // this updates with the useEffect([players]). Others don't. 
}

我也尝试过奇怪的东西,比如在 function 开头的let newPlayers = players player ,然后尝试setPlayers(newPlayers) ,但这些奇怪的解决方案都没有我试图解决手头的问题。 感觉就像我完全错过了 React 的基本内容,或者对象或其他东西上的useEffectuseState

Hands组件只是放在这里,与问题无关。 它循环播放我的Players并在我想要的屏幕上显示他们的牌。

return (
<View key={styles.playerHands}>
    {props.players.players.map((player, i) =>
        <View key={`player-${i}`} style={styles.player}>
            {player.hands.map((cards, j) =>
                <View key={`player-${i}-${j}`} style={styles.hand}>
                    {cards.map((card, k) =>
                        <Image key={`player-${i}-${j}-${k}`} style={createCardOverlap(k, j)} resizeMode={'contain'} source={displayCard(card)} />
                    )}
                </View>
            )}
        </View>
    )}
</View>
)

希望这已经得到了充分的解释,重申我正在尝试了解如何在我的Players obj 上使用setPlayers() 我很困惑如何做到这一点setPlayers({...players}); 完全剥夺了它调用任何函数的能力,并且在我调用player上的一些函数以更新其 state 之后,我不知道如何解决明显的setPlayers(players)问题。

我这样做是否正确,因为感觉就像我错过了一些东西。 所有更新 state 都发生在 App 中,我的Players obj 只是正常更新其内部 state。

谢谢

setPlayers({...players}); 创建一个新的普通 object 并用players自己的公共数据属性填充它。 不会创建Players实例; 普通的 object 没有正确的原型,也没有被Player构造函数初始化。 为此,您需要调用new Players

我发现我很少使用 state 中的对象,这些对象不仅仅是普通对象(传播适用),但有时它很方便。 如果要使用Players ,则需要更新Players以提供一种通过必要的更改创建新Players object 的方法。

FWIW,我不会有Players class,它似乎没有提供太多东西。 我只需要一个Players数组并将currentPlayer保存在 state 中。 然后让Player.prototype.hit返回一个新的、更新的Player object。 Since objects in state must not be directly modified , you'd probably want to write Player using immutable methodology (methods don't modify the state of the object, they return a new object with the updated state).


附带说明一下,“ 不要直接修改 state ”规则似乎正在hit以下几个方面:

  • 调用pop on deck直接修改了deck的 state。
  • 调用hit this.players[this.currentPlayer] (在Players.prototype.hit )直接修改Players中的播放器 object 。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM