gpt4 book ai didi

javascript - Redux:如何追踪数据是否被本地修改?

转载 作者:行者123 更新时间:2023-12-03 11:17:33 25 4
gpt4 key购买 nike

我知道这是一个有点意见的问题,而且很长,但我在 Redux 中想出一个好的解决方案时遇到了麻烦。

我正在构建一个关卡编辑器,我想向用户显示数据在保存到服务器后是否已被修改。首先,考虑数据:

chapters: [{
id: 1,
levelIds: [ 2 ]
}],
levels: [{
id: 2,
entityIds: [ 4, 5 ]
}],
entities: [{
id: 4, position:...
}, {
id: 5, position:...
}]

一个章节有多个级别,一个级别有多个实体。在关卡编辑器中,您将整个章节作为一个项目进行编辑,因此如果任何实体或关卡发生更改,则整个章节将被视为未保存。

我想跟踪自上次保存到服务器以来用户是否对数据进行了任何更改。我想在旁边显示一个 * 示例章节名称(如果有更改)。标准:

  • 跟踪未保存(未保存到服务器)状态
  • 状态必须与撤消/重做系统配合使用
  • 如果更改了某些“嵌套”数据(如实体位置),顶级章节必须知道它未保存,而不是实体本身

我已经探索了几个选项,我将尝试说明为什么我不确定是否有任何解决方案比其他解决方案更好。

选项 1:在每一章上存储一个“未保存”标志

此解决方案涉及存储一个“未保存”标志,可能在一个单独的 reducer 中,在任何修改时设置为 true,在章节保存到时设置为 false服务器。

问题

  1. 我需要跟踪很多操作,所以这有点冗长。我还需要手动跟踪哪些操作实际修改了章节。它可能看起来像:

function isUnsavedReducer( state = {}, action ):Object {
switch( action.type ) {
case CHANGE_ENTITY_POSITION:
case CHANGE_ENTITY_NAME
...etc
case CHANGE_LEVEL_TITLE: {
return {
...state,
[ action.chapterId ]: true
};
}
}
}
  1. 大多数操作不知道chapterId。例如,如果我移动一个实体, Action 看起来像 { entityId: 2, position: newPosition }。如果我走这条路,我想我必须将 chapterId 添加到所有操作,即使它们不修改章节?

选项 2:跟踪最后保存的章节对象

从表面上看,这看起来更简单。每当持久化数据时,只需存储当前内存中的章节对象:

function lastSavedReducer( state = {}, action ):Object {
switch( action.type ) {
case SAVE_CHAPTER: {
return {
...state,
[ action.chapterId ]: action.chapter
};
}
}
}

然后在 View 中检查当前数据是否未保存,这是一个严格的相等性检查:

{ lastSaved[ currentChapterId ] === this.props.chater ? 'Saved' : 'Unsaved' }

问题:

  1. 与上面的问题 #2 相同。当我使用 redux 操作修改实体位置时,我不会修改顶级章节本身。我必须修改我所有的缩减器,如 chapterReducer 以返回一个新对象(即使实际上没有任何改变)。我还可以存储“最后保存的实体”对象,但由于所有实体都保存在一个存储中,我无法跟踪哪些章节未保存,只能跟踪某些内容未保存。

我是否缺少明显的解决方案?我应该修改我的数据存储方式还是我的 reducer 设置?我的 reducer 中的规范化数据,以及许多可以设置为“未保存”的可能操作,让我不确定最好的前进方式。您是否实现过类似的东西并且已经知道其中的陷阱或最佳前进方向?

最佳答案

问题是您已经规范化了您的数据,并且这些数据充当了您应用程序的真实来源。

为什么要让还没有保存的编辑事件直接修改这个归一化的数据呢?

  • 如果不从后端重新获取保存的状态就很难恢复到保存的状态(这不应该是必需的)
  • 这很令人困惑,因为我们不知道规范化数据是否已保存(您当前的问题)

最佳解决方案:将未保存的操作保留在列表中,但在保存之前不要运行它们

您可以在商店列表中累积版本操作,而不是直接修改规范化数据。

在渲染时,您可以使用此列表投影您的 Redux 商店状态,因此您的商店状态不会改变,但您仍然可以连接到当前版本状态的组件。这种技术与事件溯源和快照非常相似。

想象一下您的商店状态是这样的:

const storeState = {
unsavedEditionActions: [a1,a2,a3],
normalizedData: {
chapters: [{
id: 1,
levelIds: [ 2 ]
}],
levels: [{
id: 2,
entityIds: [ 4, 5 ]
}],
entities: [{
id: 4, position:...
}, {
id: 5, position:...
}]
}
}

如果一个组件需要获取版本状态和原始状态,你可以直接在 mapStateToProps 中进行归约:

let Component = ...

Component = connect(state => {

return {
normalizedData: state.normalizedData,
normalizedDataEdited: state.unsavedEditionActions.reduce(myNormalizedDataReducer,state.normalizedData)
}

})(Component)

您的组件现在将接收当前保存的规范化数据和当前草稿版本数据。从那时起,您就可以施展魔法,对 2 个数据库的所有内容进行浅层比较,以了解已编辑的内容。

保存时,清空列表并将其应用于规范化数据。取消时,您只需清空此列表(无需重新获取,因为您保持了编辑前的状态)。

我建议使用 Reselect 和 ImmutableJS 以获得更好的性能,但没有它也应该可以正常工作。

请注意,这种维护事件列表的技术也是许多人用于乐观更新的技术(即:在用户操作后立即提供反馈,无需等待服务器批准,但能够恢复到之前的状态如果服务器不批准更改)。一些有用的链接:

请注意,您不会被迫在 connect 中减少操作列表,您也可以直接在您的商店中保存 2 个条目:normalizedData/normalizedDataEdited。


其他解决方案:构建一个图形以从规范化数据进行编辑,并比较 initialGraph != editedGraph

您可以构建一个临时的非规范化图形,而不是直接修改 edition 上的规范化数据。因此,您可以构建此图的副本,编辑副本,并将副本与原始图进行浅比较以查看它是否被修改。

function levelEditionReducer( state = {}, action ) {
switch( action.type ) {
case LEVEL_DATA_LOADED:
const levelData = action.payload.levelData;
return {
initialGraph: levelData,
editedGraph: levelData,
}
case CHANGE_ENTITY_NAME
const newEditedGraph = someFunctionToEditEntityName(state.editedGraph,action);
return (newEditedGraph == state.editedGraph) ? state : { ...state, editedGraph: newEditionState }
......
}
}

然后在编辑之后,您可以看到 state.initialGraph != state.editedGraph 并显示保存按钮和草稿状态。

请注意,只有当您非常小心地处理不可变数据结构并确保不更新任何不必要的内容时,这种浅层比较才有效。如果 someFunctionToEditEntityName 返回与输入相同的状态,reducer 返回完全相同的状态很重要,因为操作实际上没有改变任何东西!

用 ES6 编写这种代码并不简单,但是如果你使用其他库,如 Immutable 或 updeep 或类似的东西,如果你试图将一个对象的属性设置为它已经具有的值,它可能会使操作并确保返回原始对象。

另请注意,您会及早发现自己的疏忽,因为保存按钮会在不应该显示的时候显示 :) 所以您可能不会错过它。

数据最终保存后,您可以将保存的图形合并到规范化数据中

关于javascript - Redux:如何追踪数据是否被本地修改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38775405/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com