air*_*bag 6 javascript design-patterns graph redux react-redux
我正在尝试设计具有相当复杂状态的 webapp,其中许多单个操作应该触发跨多个组件的多个更改和更新,包括从外部端点异步获取和显示数据。
我正在构建一个混合的 cytoscape.js / redux 应用程序,用于使用图形对蛋白质相互作用进行建模。
我的 Redux 存储需要保存图形的表示(节点和边对象的集合),以及可由用户设置的众多过滤参数(即仅显示包含特定值的节点等)。
当前的实现使用 React.js 来管理所有状态,随着应用程序的增长,它变得非常单一,难以推理和调试。
以前从未使用过 Redux,在尝试从概念上设计新实现时,我有点挣扎。具体来说,我有以下问题/疑虑:
Cytoscape.js 是一个独立的组件,因为它直接操作 DOM。它期望状态(特别是节点和边集合)具有某种形状,这是嵌套的并且有点难以处理。由于对任何节点或边缘对象的每次更新都应该在 cytoscape 中以图形方式反映,我应该在 Redux 存储中镜像它期望的形状,还是应该在每次更新时转换它?如果是这样,那么什么地方是一个好地方呢?mapStateToProps 还是在减速器里面?
某些事件,例如选择节点和/或边,会在整个应用程序中产生多种副作用(从服务器异步获取数据,从选择中提取其他数据并进行转换/聚合,其中一些是派生的,一些是它来自外部 api 调用)。我在思考如何处理这些变化时遇到了麻烦。更具体地说,假设触发了 SELECTION_CHANGE 操作。它应该包含选定的对象,还是只包含它们的 ID?我猜从性能角度来看,ID 的负担会更小。更重要的是,我应该如何处理 SELECTION_CHANGE 操作所需的级联更新?单个 SELECTION_CHANGE 操作应触发 UI 和状态树的多个部分的更改。这意味着在不同的减速器上触发多个动作。
用户需要能够根据任意谓词过滤和操作图。更具体地说,他应该能够永久删除\添加节点和边,并将视图限制在图形的特定子集上。换句话说,一些更改是永久性的(删除\添加或以其他方式编辑图形),而其他更改仅与显示的内容相关(例如,仅显示表达水平高于某个阈值的节点等)。我应该在我的状态树中保留一个单独的、“过滤”的图形副本,还是应该针对过滤参数的每次更改动态计算它?和以前一样,如果是这样,哪里是执行这些过滤操作的好地方:mapStateToProps、reducers 或其他我没有想到的地方?
我希望这些高层次和抽象的问题足以描述我想要实现的目标,如果不是,我很乐意详细说明。
Redux 状态形状的推荐方法是使状态尽可能最小化,并根据需要从中派生数据(通常在选择器函数中,可以在组件的mapState和其他位置(例如 thunk 动作创建者或 sagas)中调用)。对于嵌套/关系数据,如果将其存储在规范化结构中并根据需要对其进行非规范化,效果最佳。
虽然你在行动中投入的内容取决于你自己,但我通常更喜欢将它们保持在最低限度。这意味着在操作创建器中查找必要的项目及其 ID,然后查找数据并在减速器中执行必要的工作。至于减速器处理,有多种方法可以实现。如果您采用“组合切片缩减器”方法,该combineReducers实用程序将为每个切片缩减器提供响应给定操作的机会,并根据需要更新其自己的切片。您还可以编写更复杂的减速器,在状态树的更高级别上运行,并根据需要自行执行所有嵌套更新逻辑(如果您使用“功能文件夹”类型的项目结构,则这种情况更常见)。无论哪种方式,您应该能够使用一个分派操作来完成单个逻辑操作的所有更新,尽管有时您可能希望连续分派多个连续操作来执行更高级别的操作(例如 -> UPDATE_ITEM- APPLY_EDITS>CLOSE_MODAL处理单击编辑弹出窗口上的“确定”按钮)。
我鼓励您通读 Redux 文档,因为它们涉及其中许多主题,并指出其他相关信息。特别是,您应该阅读新的“结构化减速器”部分。请务必阅读“先决概念”页面中链接的文章。Redux FAQ还指出了大量相关信息,特别是“ Reducers”、“组织状态”、“代码结构”和“性能”类别。
最后,还有一些其他相关链接。我在https://github.com/markerikson/react-redux-links上保留了大量有关 React、Redux 和相关主题的高质量教程和文章的链接。从那里链接了很多有用的信息。我也是Redux-ORM库的忠实粉丝,它提供了一个非常好的抽象层来管理 Redux 存储中的规范化数据,而无需尝试更改 Redux 的特殊之处。