【问题标题】:How to model recursively nested data in state如何对状态中的递归嵌套数据进行建模
【发布时间】:2020-04-07 15:09:14
【问题描述】:

我的数据结构类型如下:

export interface IGroup {
  id: number;
  name: string;
  groupTypeId: number;
  items: IItem[];
  groups: IGroup[];

}

它递归地表示“Group”和“Group”以及“Group”和“Item”之间的多对多关系。组由项目和子组组成。项仅派生为简单类型和其他元数据,但不能有子项。单个组代表层次结构的顶部。

我目前有组件、钩子等递归地获取单个组并创建一个编辑/创建表单,如下所示:

我有这个表格与测试数据一起“工作”以在保存时生成如下标准数据输出:

{
  "1-1": {
    "name": "ParentGroup",
    "groupType": 2
  },
  "2-4": {
    "name": "ChildGroup1",
    "groupType": 1
  },
  "2-9": {
    "name": "ChildGroup2",
    "groupType": 3
  },
  "2-1": {
    "itemType": "FreeForm",
    "selectedName": "Testing",
    "selectedClass": 5
  },
  "2-2": {
    "itemType": "FreeForm",
    "selectedName": "DisplayTest",
    "selectedClass": 5
  },
  "3-4": {
    "itemType": "EnumValue",
    "selectedItem": {
      "id": 12900503,
      "name": "TRUE"
    }
  },
  "3-5": {
    "itemType": "EnumValue",
    "selectedItem": {
      "id": 12900502,
      "name": "FALSE"
    }
  },
  "3-9": {
    "itemType": "FreeForm",
    "selectedName": "Test",
    "selectedClass": 5
  },
  "3-10": {
    "itemType": "FreeForm",
    "selectedName": "Tester",
    "selectedClass": 5
  },
  "3-11": {
    "itemType": "FreeForm",
    "selectedName": "TestTest",
    "selectedClass": 5
  }
}

这些对象的“关键”是网格列和行,因为没有其他保证的唯一标识符(如果用户正在编辑,则预期组在数据库中具有 id,但如果用户正在添加新的表单中的组。否则,名称是可以更改的输入表单。)这很有意义,并且很容易以这种方式对键进行建模。如果将另一个组或项添加到层次结构中,则可以添加它的列和行。

我遇到的问题是我希望能够有一个添加按钮,该按钮将添加到组项目或组数组中,以便可以创建层次结构中的新行。我的表格应该处理这些新条目。 例如。

"1-1": {
  groups: [..., {}],
  items: [..., {}]
}

但我拥有的唯一数据结构是深度嵌套的 IGroup。这不利于用作状态和添加到这种深度嵌套的状态。

我遇到的另一个问题是我需要能够将项目和组映射到它们的位置,以便我可以将多对多表转换为相应的数据库并插入新的组/项目。

建议的解决方案: 我在想,与其将一个组放入我的递归组件中,不如创建规范化的对象来存储状态。我将拥有一个由列行键控的对象,该对象将保存所有组。另一个由列行键控以保存所有项目。然后我想我需要另外两个对象来保存多对多的关系,例如 Group to Group 和 Group to Item。

从表单中获取数据后,我希望可以遍历这些状态对象,以这种方式找到层次结构并将必要的数据发布到数据库。

我看到有很多数据结构来保存这些数据,但鉴于我的建模结构,我不确定这是否是完成此任务的最佳方式。我也刚开始使用 Redux Toolkit,所以我对 reducer 有点熟悉,但还不足以了解如何在这里应用它们来帮助我。我一直在努力解决这个问题,任何帮助或指导使这更容易将不胜感激。

【问题讨论】:

  • 您找到解决方案了吗?

标签: reactjs recursion react-redux recursive-datastructures


【解决方案1】:

进行标准化。每个实体都有一个单一的事实来源,这使得读取和写入状态变得更加容易。

为此,请尝试normalized-reducer。这是一个简单的高阶归约器,学习曲线低。

这是一个有效的CodeSandbox example of it 实现与您的问题非常相似的组/项目复合树。

基本上,您将定义树的架构:

const schema = {
  group: {
    parentGroupId: { type: 'group', cardinality: 'one', reciprocal: 'childGroupIds' },
    childGroupIds: { type: 'group', cardinality: 'many', reciprocal: 'parentGroupId' },
    itemIds: { type: 'item', cardinality: 'many', reciprocal: 'groupId' }
  },
  item: {
    groupId: { type: 'group', cardinality: 'one', reciprocal: 'itemIds' }
  }
};

然后传入库的顶层函数:

import normalizedSlice from 'normalized-reducer';

export const {
  emptyState,
  actionCreators,
  reducer,
  selectors,
  actionTypes,
} = normalizedSlice(schema);

然后将reducer 连接到您的应用程序(与React useReducer 和Redux 存储reducers 一起使用),并使用selectorsactionCreators 来读取和写入状态。

【讨论】:

  • 感谢您的回答!这看起来可能对我有用,只要我可以连接到它来为我的 react-hook-forms 提供动力。如果我要在您发布的代码沙箱中添加“删除”按钮,actionCreators.delete 会自动删除项目或组并将其与所有相关实体分离吗?
  • 是的,删除会自动分离实体,如果您需要级联删除(删除所有祖先节点),请提供cascade 参数:github.com/brietsparks/normalized-reducer#delete
  • 非常感谢!如果我想加载初始递归结构,你会建议我自己滚动递归结构并批量创建和附加语句吗?或者我可以使用规范化器向切片的状态添加递归结构吗?
  • 有没有一个使用规范化器和规范化减速器的例子?我在理解如何在两者之间转换模式时遇到了一些麻烦。就像我的原始数据被深度非规范化一样,但规范化 reducer 的架构假设我以规范化的方式使用 id。另外,我不确定如何在不循环遍历新创建的实体的情况下将新的规范化结构添加到我的状态
猜你喜欢
  • 2017-11-28
  • 2016-09-09
  • 2016-08-31
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
  • 2017-07-02
  • 2014-05-08
  • 1970-01-01
相关资源
最近更新 更多