反应:状态对象中缺少属性

亚历山大·克劳兹(Alexander Krauze)

因此,我开始学习React,并做了一些基础知识(计数器,待办事项列表)后,我决定做一些更具挑战性的事情。带有棋子的棋盘,可以通过单击来移动。我的主要组成部分是Chessboard然后渲染Tiles可能包含的渲染Piece有关游戏的信息以Chessboard's状态存储,如下所示:

interface State {
  board: Record<string, pieceInterface>;
  isPieceMoving: boolean;
  movingPieceId: string;
}

我用Chessboard's方法来处理移动的部分onTileClick(id: string)Tile作为道具传递给a ,并在单击图块时使用图块ID对其进行调用(例如“ a4”,“ f6”,“ h3”)。它具有以下逻辑。游戏可以处于两种状态:我当前正在移动一个棋子,或者我目前什么都不做,然后通过单击它开始移动一个棋子。当我开始移动作品时,我将其ID(或者更确切地说是女巫架子上的瓷砖的ID)存储在中state.movingPieceId当我尝试放置它时,我会检查图块是否为空,然后进行相应的更改state.board这是我的代码:

onTileClick = (id: string): void => {
  if (!this.state.isPieceMoving) {
    if (this.state.board[id]) {
      this.setState({
        isPieceMoving: true,
        movingPieceId: id,
      });
    }
  } else {
    if (!this.state.board[id]) {
      this.setState((state) => {
        let board: Record<string, pieceInterface> = state.board;
        const piece: pieceInterface = board[state.movingPieceId];

        delete board[state.movingPieceId];
        board[id] = piece;

        // console.log(board[id]);
        // console.lob(board);

        const isPieceMoving: boolean = false;
        const movingPieceId: string = "";

        return { board, isPieceMoving, movingPieceId };
      });
    }
  }
};

第一部分工作正常。但是在第二个中,有一些我找不到的错误。当我取消注释此控制台日志时,输出看起来像这样(我想将片段从“ a2”移动到“ a3”):

Object { "My piece representation" }
Object {
    a1: { "My piece representation" },
    a3: undefined,
    a7: { "My piece representation" },
    ...
}

我的代码正确地从“ a2”(板上没有“ a2”属性)“获取”了单击的片段,但显然并没有将其放回原处。即使打印时board[id]给出正确的对象!我将日志记录console.log(board)向上移动,以查看发生错误的位置。即使我将它直接放在其他位置,它仍然说“ a3”是undefined

我花了几个小时试图了解正在发生的事情。我试图在控制台中模拟这段代码片段,但它始终可以正常工作!有人可以向我解释我在做什么错吗?这是我还没有学过的一些怪异的React的setState机制吗?还是我不知道的一些基本的javascript内容?我们会提供任何帮助,因为我被困在这里无法继续前进并做其他任何事情。

编辑#1

好的。所以,我是能够解决受到此问题的深拷贝 state.boardboard(由@Linda Paiste的评论inspiredy)。我只是使用了简单的JSON stringify / parse hack:

let board: Record<string, pieceInterface> = JSON.parse(JSON.stringify(state.board));

这修复了该错误,但我不知道为什么。我将继续公开这个问题,所以也许有人会向我解释我先前的代码出了什么问题以及为什么出了问题。

编辑#2

好的。感谢@Linda的解释和阅读文档,我终于了解了我做错了什么。琳达在下面的回答(被接受的答案)中做了详细的解释。非常感谢大家!

可爱的女皇

问题:状态突变

let board: Record<string, pieceInterface> = state.board;

您正在创建一个新变量board,该变量引用的板对象与实际状态下的板对象相同。然后,您对该对象进行突变:

delete board[state.movingPieceId];
board[id] = piece;

这是非法的状态变更,因为对状态的任何更改board也会影响状态this.state.board

解决方案:创建一个新板

您要在状态中更改的任何数组或对象都必须是该对象的新版本。

您的board作品的深层副本,因为此新作品board完全独立于您,state因此您可以对其进行变异而不会影响状态。但这不是最好的解决方案。它效率低下,并且会导致不必要的渲染,因为每个块对象也将是新版本。

我们只需要复制正在更改的对象:stateboard

this.setState((state) => ({
  isPieceMoving: false,
  movingPieceId: '',
  board: {
    // copy everything that isn't changing
    ...state.board,
    // remove the moving piece from its current position
    [state.movingPieceId]: undefined,
    // place the moving piece in its new location
    [id]: state.board[state.movingPieceId],
  }
}));

您的打字稿类型的board属性State应该是Partial<Record<string, pieceInterface>>因为并非板上的每个插槽都有一块。

非常丑陋但功能强大的Code Sandbox演示

编辑:关于setState回调

我听说setState与函数配合使用可state复制this.state,因此可以对其进行突变。

那是不对的。您永远都不应改变状态,这也不例外。使用回调的优点state是保证参数是当前值,这很重要,因为它setState是异步的,并且可能有多个挂起的更新。

这是React文档说的(重点已添加):

state在应用更改时对组件状态引用它不应该直接突变。相反,改变应该通过建立一个代表新对象基于从输入stateprops“。

二者stateprops通过更新功能接收保证是向上的更新。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

反应状态-更新嵌套数组中对象的属性

来自分类Dev

反应-更新状态中的对象

来自分类Dev

反应-通过存在的对象属性覆盖状态

来自分类Dev

无法访问动态对象属性并在反应中存储到状态

来自分类Dev

如何在反应中更新状态对象?

来自分类Dev

Vuex 状态对象长度对计算属性没有反应

来自分类Dev

反应状态嵌套属性

来自分类Dev

与对象反应状态挂钩

来自分类Dev

从对象数组中删除对象的元素-反应状态

来自分类Dev

如何在对象状态反应中删除对象数组

来自分类Dev

反应数组中的计数对象属性

来自分类Dev

更新反应中对象列表的某些属性

来自分类Dev

反应导航和打字稿。类型中缺少属性“getScreen”

来自分类Dev

使用反应中的先前状态更新对象数组中的数组

来自分类Dev

无法在反应中显示存储在状态对象中的数据

来自分类Dev

在反应中更新状态属性的有效方法?

来自分类Dev

如何在反应钩子中更新状态的单个属性?

来自分类Dev

修改状态对象中的属性的正确方法?

来自分类Dev

在状态中查找对象并更新属性

来自分类Dev

反应setState更改状态中的其他对象

来自分类Dev

在反应状态下替换数组中的对象

来自分类Dev

在本机反应中,null不是组件状态的对象

来自分类Dev

如何使用钩子在反应中更新嵌套 json 对象的状态

来自分类Dev

如何在反应中更新状态的对象数据

来自分类Dev

如何在反应中为嵌套的对象数组设置状态?

来自分类Dev

对所需对象中的数组属性反应默认属性

来自分类Dev

具有计算对象属性名称的嵌套对象分解-反应状态

来自分类Dev

设置状态模式以访问状态中的对象属性(reactjs)

来自分类Dev

反应状态是作为反应元素的对象数组

Related 相关文章

  1. 1

    反应状态-更新嵌套数组中对象的属性

  2. 2

    反应-更新状态中的对象

  3. 3

    反应-通过存在的对象属性覆盖状态

  4. 4

    无法访问动态对象属性并在反应中存储到状态

  5. 5

    如何在反应中更新状态对象?

  6. 6

    Vuex 状态对象长度对计算属性没有反应

  7. 7

    反应状态嵌套属性

  8. 8

    与对象反应状态挂钩

  9. 9

    从对象数组中删除对象的元素-反应状态

  10. 10

    如何在对象状态反应中删除对象数组

  11. 11

    反应数组中的计数对象属性

  12. 12

    更新反应中对象列表的某些属性

  13. 13

    反应导航和打字稿。类型中缺少属性“getScreen”

  14. 14

    使用反应中的先前状态更新对象数组中的数组

  15. 15

    无法在反应中显示存储在状态对象中的数据

  16. 16

    在反应中更新状态属性的有效方法?

  17. 17

    如何在反应钩子中更新状态的单个属性?

  18. 18

    修改状态对象中的属性的正确方法?

  19. 19

    在状态中查找对象并更新属性

  20. 20

    反应setState更改状态中的其他对象

  21. 21

    在反应状态下替换数组中的对象

  22. 22

    在本机反应中,null不是组件状态的对象

  23. 23

    如何使用钩子在反应中更新嵌套 json 对象的状态

  24. 24

    如何在反应中更新状态的对象数据

  25. 25

    如何在反应中为嵌套的对象数组设置状态?

  26. 26

    对所需对象中的数组属性反应默认属性

  27. 27

    具有计算对象属性名称的嵌套对象分解-反应状态

  28. 28

    设置状态模式以访问状态中的对象属性(reactjs)

  29. 29

    反应状态是作为反应元素的对象数组

热门标签

归档