如何处理不一致的 mobx-state-tree 快照?

sto*_*fln 5 javascript mobx mobx-state-tree

我正在编写一个电子应用程序,并将所有应用程序数据保存在一个 MST 树中。现在我注意到,时不时地您会遇到数据变得不一致的情况(缺少引用对象等)。虽然任何类型的数据库都可能发生这种情况,但我发现 MST 存在一个特殊问题:

由于我们有一棵树在应用程序启动时被反序列化,然后用作单个快照,因此单个不一致将导致整个应用程序失败。我的应用程序将无法获得任何数据。

关于如何处理这个问题有任何提示吗?

更多信息

目前,每次树发生变化时(onSnapshot),我都会创建一个快照并将其保存在 localStorage 中。因此,错误用例是:创建 mst 对象 -> 在树的其他部分创建引用 -> 删除 mst 对象 -> 触发 onSnapshot -> 损坏的树被持久化。重新加载应用程序不会有帮助,因为树持续处于损坏状态。

etu*_*dor 5

为了避免传入数据不一致,我在模型中添加了默认值。例如

const collectionModel = types.model({
  type: types.optional(types.literal('collections'), 'collections'),
  preview: types.optional(
    types.model({
      products: types.array(SelectableProduct)
    }),
    {}
  ),
  data: types.optional(types.model({
    items: 24,
    perRow: 4,
    global: types.optional(EvergreenQuery, {}),
    curated: types.array(EvergreenItemSettings)
  }), {})
})
Run Code Online (Sandbox Code Playgroud)

collectionModel这将允许我从空对象创建一个实例

collection1 = collectionModel.create({})
Run Code Online (Sandbox Code Playgroud)

当您使用引用时,请确保使用safeReference 来自文档

 * `types.safeReference` - A safe reference is like a standard reference, except that it accepts the undefined value by default
 * and automatically sets itself to undefined (when the parent is a model) / removes itself from arrays and maps
 * when the reference it is pointing to gets detached/destroyed.
 *
 * Strictly speaking it is a `types.maybe(types.reference(X))` with a customized `onInvalidate` option.
Run Code Online (Sandbox Code Playgroud)

因此,如果您要删除存储中其他位置引用的节点,则该引用将设置为未定义。根据我的经验,损坏的引用特别难以调试。

我喜欢 mobx-state-tree 迫使我有一个明确的结构。这让我在编写逻辑之前先考虑一下结构,从而简化了逻辑的编写。

一个黑客解决方案

您可以做的一个技巧是在保存快照之前实例化一个新模型。如果成功则保存快照,如果不成功则跳过它。

const MyModel = types.model({})

onSnapshot(myModelInstance, s => {
  try {
    const testModel = MyModel.create(s)
    if (localStorage) {
      // here you can save the snapshot because you know for sure it won't break

      localStorage.setItem('snap', JSON.stringify(s))
    }
  } catch(e) {
    // log or something

    // OR
    console.log(
      'snapshot failed because of',
       difference(s, JSON.parse(localStorage.getItem('snap'))
    )
  }
})



// this methos does a deep difference between two objects
export const difference = <T>(orObject: object, orBase: object): T => {
  function changes(object: any, base: any): any {
    return _.transform(object, function(
      result: any,
      value: any,
      key: any
    ): any {
      if (!_.isEqual(value, base[key])) {
        result[key] =
          _.isObject(value) && _.isObject(base[key])
            ? changes(value, base[key])
            : value;
      }
    });
  }

  return changes(orObject, orBase);
};
Run Code Online (Sandbox Code Playgroud)

diff 方法非常有用,因为它可以更轻松地找出崩溃的原因。这样,localStorage 将只有有效的快照,任何无效的快照都会记录问题的原因。


小智 1

反序列化的数据来自哪里?我对 Electron 不太熟悉,但我想您在应用程序会话之间将其存储在本地(通过快照 mst 树)。

  1. 我的第一个预感是看看当你序列化它时会发生什么?也许在保存快照之前验证快照是个好主意(我想在应用程序关闭时?)?

  2. 这种不一致是否“一致”?我的意思是 - 是树的同一部分引起的吗?也许拆分树 - 序列化 -> 反序列化不同部分的多个快照,而不是 1 个大的东西。

  3. 虽然我确实使用mst,但我不使用快照,至少不使用整个树级别,检查创建快照时树是否被冻结..也许,我认为不太可能,在快照时仍然在树上进行更改正在编写中。