我如何在mobx可观察的地图中观察到任何变化(添加,移除或更改的属性)?

时间:2019-02-06 18:39:29

标签: mobx mobx-react

class FilterCriteria {
    @observable filter = new Map();
}

let criteria = new FilterCriteria ();

// setting up a reaction when something in the filter changes
// (property added, removed, or changed)
reaction(()=>criteria.filter, data => console.log(data.toJSON()));

criteria.filter.set('name', 'John'); // setting a new property.

我希望上面的代码能打印出{ 'name': 'John' },但似乎反应没有运行。

我怀疑我以错误的方式设置了反应。每当添加新键,删除现有键或更改键值时,我都想做出反应。我在编译时不知道键或值。

我应该怎么做?

更新

我将代码更改为

class FilterCriteria {
    @observable filter = new Map();
    @computed get json(){ return this.filter.toJSON(); }
}
...
reaction(()=>criteria.json, data => console.log(data));

,现在看来可以正常工作。每当我在地图中添加,删除或更改值时,都会执行反应副作用。

所以问题是,为什么反应在第二个示例中执行而在第一个示例中没有执行?

更新2

我再次更改了代码。我恢复到几乎第一个版本,但是这次而不是对criteria.filter做出反应并记录data.toJSON(),而是对criteria.filter.toJSON()做出反应,并登录data(toJSON从副作用中移出了到所观察的值)。这次反应正常进行。

class FilterCriteria {
    @observable filter = new Map();
}

reaction(()=>criteria.filter.toJSON(), data => console.log(data));

再次,我不明白为什么。如果criteria.filter本身不是可观察的,那么当 inside 条件.filter发生变化时,如何重新评估监视的表达式?

更新4(希望最后一个)解决方案

根据MobX文档,mobx在执行跟踪函数期间对读取的现有可观察属性做出反应。

当可观察属性更改时,会执行

反应副作用。在我的示例中,当对criteria.filter做出反应时,此处读取的observable属性为filter,但过滤器本身从未更改。总是同一张地图。过滤器的属性会发生变化。因此,永远不会对criteria.filter进行反应。

但是当我对criteria.filter.toJSON()mobx.toJS(criteria.filter)做出反应时,该反应已正确执行。

那为什么呢? criteria.filter不变,并且toJSON不是可观察的属性。它是一个功能。 mobx.toJS也是一样。似乎这里没有读取任何属性。但这是不正确的。正如文档所述(但不是很强调),执行toJSON或mobx.toJS时确实会读取criteria.filter的属性,因为这两个函数都创建了地图的深层克隆(因此遍历每个属性)。

现在,在开始时,地图不包含任何属性。那么,如何跟踪新添加的属性,因为开始跟踪时它们不存在(无法读取)?这是地图的功能。地图还为尚不存在的属性提供了可观察性。

在MobX 5中,您也可以跟踪可观察的对象的不存在属性(不是类实例),只要它们被observable或observable.object实例化。类实例不支持此功能。

1 个答案:

答案 0 :(得分:1)

在mobx中,当您要观察可观察到的变化时,有两个选择。 reactionobserveReaction允许您指定何时可观察的特定方面发生更改时要调用某些函数。这可能是对数组长度,键,属性或其他任何东西的更改。 observe会在可观察对象发生更改时触发一些功能。

我怀疑未触发您的反应的原因是由于第一个功能。 () => criteria.filter。添加/删除键或更改值时,不会触发此操作。相反,它将在filter实际更改时被触发。而且由于filter实际上是对地图的引用,所以即使地图本身发生变化,它也永远不会改变。

以下是一些例子来说明我的观点:

如果要在添加或删除键时触发反应,则可能希望函数为:

() => criteria.filter.keys()

添加或删除键后,此功能的结果将有所不同。同样,如果您想在修改值时触发响应,则应执行以下操作:

() => criteria.filter.values()

因此,这两个组合应该是您侦听键/值更改所需要的。或者,您可以使用observe,它会在每次更改时触发,并要求您检查已更改的内容,以确保满足特定条件以保证调用函数(即键/值更改)

更新:这是一个说明问题的示例

@observable map = new Map();

让我们说在内存中map的值为5。因此,当您检查map === map时,它等效于5 === 5并将计算为true。

现在,查看您发布的第一个代码段:

reaction(() => map, data => console.log(map.toJSON()));

每次添加/删除键或更改值时,第一个功能将运行。结果将是5,因为这就是我们在本例中所说的内存中的值。它会说:旧值是5,新值是5,所以没有变化。因此,该反应将不会运行第二个功能。

现在是第二个片段:

reaction(() => map.toJSON(), data => console.log(data));

首先,该函数的结果将为:{},因为Map为空。现在让我们添加一个密钥:

map.set(1, 'some value');

现在,第一个函数的结果将是:

{"1": "some value"}

很明显,此值与{}不同,因此发生了某些变化,并调用了反应的第二个功能。