onBecomeObserved is not called
Intended outcome:
I want to watch for observation of some property and do network requests when some other property allows it.
Actual outcome:
onBecomeObserved is not called, but I clearly accessing property in computed property.
How to reproduce the issue:
Code:
const events: string[] = [];
const item = makeAutoObservable({
canLoad: false,
value: undefined as number | undefined,
error: undefined as Error | undefined,
retry: () => {
item.error = undefined;
item.canLoad = true;
},
});
let disposeReaction = noop;
onBecomeObserved(item, 'value', () => {
events.push('valueBO');
disposeReaction = reaction(
() => item.canLoad,
isCanLoad => {
if (!isCanLoad) {
return;
}
runInAction(() => {
item.canLoad = false;
item.value = 42;
});
},
{ fireImmediately: true },
);
});
onBecomeUnobserved(item, 'value', () => {
events.push('valueBU');
disposeReaction();
});
const parentItem = makeAutoObservable({
get hasError() {
return Boolean(item.error);
},
get isLoading() {
return item.value === undefined;
},
});
const andAnotherItem = makeAutoObservable({
get see() {
if (parentItem.hasError) {
events.push('seenError');
} else if (parentItem.isLoading) {
events.push('seenLoading');
} else {
events.push(`seenValue ${item.value}`);
}
return undefined;
},
});
autorun(() => {
void andAnotherItem.see;
});
await sleep(0);
runInAction(() => {
item.error = new Error();
});
await sleep(0);
runInAction(() => {
item.retry();
void parentItem.isLoading;
when(() => !parentItem.isLoading);
});
await sleep(50);
expect(events).toStrictEqual([
'valueBO',
'seenLoading',
'seenError',
'valueBU',
'seenLoading',
'valueBO',
'seenValue 42',
]);
It gives following error:
- Expected - 2
+ Received + 0
@@ -2,8 +2,6 @@
"valueBO",
"seenLoading",
"seenError",
"valueBU",
"seenLoading",
- "valueBO",
- "seenValue 42",
seenLoading indicates, that autorun is accessing parentItem.isLoading, but valueBO is not called.
Then cryptic magic (for me, at least) happens:
- Commenting
void parentItem.isLoadinginside action fixes issue - Commenting
when(() => !parentItem.isLoading)inside action fixes issue (but we are leavingvoid ..., so only one of this lines can exist) - Commenting
disposeReaction()insideonBecomeUnobservedfixes issue
Versions
"mobx": "6.13.7",
Thanks
I think that this is clearly bug, because if obBecomeObserved is not called -- change of value that used inside computed should do nothing (because it is not become observed). Looks like computed value is cached inside action, then body of getter not called in autorun and there is no onBecomeObserved trigger. But if after final reaction I will add following code:
runInAction(() => {
item.value = 12;
});
seenValue 12 will be printed, because isLoading will be recomputed... but value was "not observed", because obBecomeObserved was not called, so isLoading should not recompute.
So I expect, that if mobx observing some value -- then onBecomeObserved/onBecomeUnobserved must indicate it. Even if something is cached -- it should make public state of observing consistent with internal (currently for me looks like mobx lies about observed state of value).