3

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

由于我们有一个在应用程序启动时反序列化的树,然后用作单个快照,因此单个不一致将导致整个应用程序失败。我的应用程序没有一条数据可用。

关于如何处理的任何提示?

更多信息

目前,我在每次树更改(onSnapshot)时创建一个快照并将其保存在 localStorage 中。所以一个错误用例是:创建 mst 对象 -> 在树的其他部分创建引用 -> 删除 mst 对象 -> onSnapshot 被触发 -> 损坏的树被持久化。重新加载应用程序无济于事,因为树一直处于损坏状态。

4

2 回答 2

3

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

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)
  }), {})
})

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

collection1 = collectionModel.create({})

当您使用参考时,请确保使用safeReference From the docs

 * `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.

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

我喜欢 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);
};

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

于 2019-09-11T10:02:07.500 回答
1

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

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

  2. 不一致是否“一致”?我的意思是 - 它是导致它的树的同一部分吗?也许拆分树-序列化->反序列化不同部分的多个快照,而不是一件大事。

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

于 2019-09-03T08:49:33.507 回答