【问题标题】:Redux - Removing object from array nested in another arrayRedux - 从嵌套在另一个数组中的数组中删除对象
【发布时间】:2026-01-11 17:00:01
【问题描述】:

我的 Redux 状态存储了一个 comments 对象,该对象存储了一个 cmets 数组。每个comment 对象中都有一个replies 数组。我将父评论 ID 和回复 ID 都传递给我的 reducer,目的是从回复数组中删除回复。

我的* cmets 对象的简化版本如下所示:

{
  "count": 4,
  "data": [
    {
      id: 123,
      text: 'This is a comment',
      replies: [
        {
          id: 479,
          text: 'This is a reply',
        },
        {
          id: 293,
          text: 'This is another reply',
        },
      ],
    },
    {
      id: 728,
      text: 'This is another comment',
      replies: [
        {
          id: 986,
          text: 'This is a reply',
        },
      ],
    },
  ],
  "pageSize": 5,
  cursor: "",
}

这是我的减速器,它似乎将父评论对象包装在一个数组中并展平回复(显然不是预期的结果,但我不知道解决嵌套数组的最佳方法)。

case types.DELETE_REPLY_SUCCESS: {
  const content = Object.assign({}, state);
  content.data = content.data.map((comment) => {
    const newObj = { ...comment };
    if (newObj.id === action.parentCommentId) {
      return newObj.replies.filter(reply => reply.id !== action.replyId);
    }
    return newObj;
  });
  return content;
}

【问题讨论】:

    标签: javascript arrays ecmascript-6 redux


    【解决方案1】:

    上面的答案比较直接,但我想分享一些我的想法。

    首先,this Redux 中的规范化状态链接解决了对我来说是个大问题...处理嵌套数据结构...您的代码变得非常复杂。 90% 的减速器应该非常简单。 嵌套数据结构变得复杂

    虽然 normalizer 是一个用于规范化状态的非常标准的工具,但我建议您首先编写自己的工具。我从这样做中学到了很多东西。

    什么是标准化数据结构?

    它是平的。它通常是相关的。

    Dan Abramov(Redux 的开发者)建议将数据片段分组存储。因此,您的 cmets 成为一个组,而您的回复共享另一个组。就像在关系数据库中一样。每个“组”都有自己的“表”。

    起初这对我来说似乎违反直觉,因为感觉就像您在编写更多数据结构。你不是……而且付出的代价是值得的。

    所以你会像这样存储你的数据

    {
      "comments": {
        "allIds" : [1,2,3,4],
        "byId": {
          1: {
            replies: [12],
            comment: "a comment"
          },
          2: {
            replies: [13],
            comment: "another comment"
          },
          3: {
            replies: [14],
            comment: "one more comment"
          },
          4: {
            replies: [15],
            comment: "oh look, a comment"
          },
        }
      },
      "replies" : {
        "allIds" : [12,13,14,15],
        "byId": {
          12: {
            comments: [1],
            reply: "a reply"
          },
          13: {
            comments: [2],
            reply: "another reply"
          },
          14: {
            comments: [3],
            reply: "yet another reply"
          },
          15: {
            comments: [4],
            reply: "a reply"
          }
      }
    }
    

    为什么重要?

    这在哪些方面让生活更轻松?首先,如果我们想要显示一个 cmets 列表,我们可以通过 allIds 数组使用 map() 并使用第一个参数来访问获取数据的键。

    这意味着我们可以遍历单个数组而不是嵌套对象。

    以同样的方式回答您的问题,您可以使用filter() 而不是map() 来删除元素。我不会在这里费心解释过滤器..找到比我能解释的任何东西都好的例子需要十秒钟。

    然后确保您遵循标准的 Redux 做事方式。

    1. 使用 reducer 复制您的数据结构。您的状态应该在没有数据的情况下初始化...这样您就知道自己做对了。

    2. 编写处理特定状态的 reducer(cmets reducer 等)对我来说,这些通常包含返回状态或返回新状态的 switch 语句。

    3. 编写为 reducer 提供所需新数据的操作。 始终遵循让函数处理一项工作的 JS 原则。 动作不应做多件事。

    注意但是使用 thunk 之类的中间件,动作可以调度其他动作,这可能会导致逻辑异常!

    评论的删除操作可能看起来很简单

    function deleteComment(commentId) {
      return {
        type: "delete project",
        payload: commentId
      }
    }
    

    这为我们提供了我们所需要的一切。 通过在我们的reducer 中使用前面提到的switch 语句,我们可以检查type 的action 正在被调度。 (这告诉我们如何处理提供的数据)以及要删除的元素,即有效负载。

    您可以按照这种简单的方法满足 85% 的应用需求。

    我知道 Redux 远不止这些,但这种方法确实帮助我掌握了如何查看 Redux 以及如何管理和操作状态。 我强烈建议阅读上面链接的 Dan Abramov 的整个 eggheads.io 教程。他在详细解释几乎所有内容方面做得非常出色。

    我希望这个对您简短问题的长回答会有所帮助。

    【讨论】:

      【解决方案2】:

      在 redux 中更新嵌套结构可能非常讨厌。我建议使用更扁平的结构。您可以使用normalizer 或手动执行此操作。

      但是,如果您需要更新现有结构,请执行以下步骤:

      1. 找到您要更改的评论的索引。
      2. 为评论创建一个新的过滤回复数组。
      3. 通过将旧评论替换为包含新回复数组的新评论来创建新数据数组。
      4. 使用新数据数组创建新状态对象。

      示例

      const state = {"count":4,"data":[{"id":123,"text":"This is a comment","replies":[{"id":479,"text":"This is a reply"},{"id":293,"text":"This is another reply"}]},{"id":728,"text":"This is another comment","replies":[{"id":986,"text":"This is a reply"}]}],"pageSize":5,"cursor":""};
      
      const action = {
        parentCommentId: 123,
        replyId: 479
      };
      
      const deleteReplySuccess = () => {
        const data = state.data;
        
        // find the index of the comment you want to update
        const commentToChangeIndex = data.findIndex(({ id }) => id === action.parentCommentId);
        
        // if it doesn't exist return the state
        if(commentToChangeIndex === -1) {
          return state;
        }
        
        // get the comment
        const comment = data[commentToChangeIndex];
        
        // create an updated comment with filtered replies array
        const updatedComment = {
          ... comment,
          replies: comment.replies.filter(reply => reply.id !== action.replyId)
        };
      
        // create a new state using object spread or assign
        return {
          ...state,
          data: [ // create a new data array
            ...data.slice(0, commentToChangeIndex), // the comments before the updated comment
            updatedComment, // the updated comment
            ...data.slice(commentToChangeIndex + 1) // the comments after the updated comment
          ]
        };
      };
      
      console.log(deleteReplySuccess());

      【讨论】:

      • 非常感谢。这几天我一直在寻找这个。由于我是 Redux 的新手,我一无所知,甚至不知道要搜索什么,当我终于开始找到一些时,我找不到任何具有对象数组和嵌套对象数组的东西。非常感谢。