Skip to content

Commit 01211a6

Browse files
authored
Fix ComputedValue fails to trigger onBecomeObservable when used in action -> autorun (mobxjs#4700)
* fix: observe computed dependencies gained in actions * fix: avoid hooks for temporary computed observers * test: simplify computed lifecycle regression
1 parent 5dbb04a commit 01211a6

3 files changed

Lines changed: 49 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"mobx": patch
3+
---
4+
5+
Fix `onBecomeObserved` not firing for dependencies gained when an observed computed is recomputed inside an action.

packages/mobx/__tests__/base/become-observed.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,43 @@ test("onBecomeObserved cascades through a chain of cached computeds #4547", () =
615615
disposeAutorun!()
616616
expect(events).toEqual(["BO", "BUO"])
617617
})
618+
619+
test("onBecomeObserved fires for a dependency gained by an observed computed in an action #3674", () => {
620+
const events: string[] = []
621+
const enabled = observable.box(false)
622+
const resource = observable.box(1)
623+
onBecomeObserved(resource, () => events.push("BO"))
624+
onBecomeUnobserved(resource, () => events.push("BUO"))
625+
const derived = computed(() => (enabled.get() ? resource.get() : null))
626+
const disposeAutorun = autorun(() => {
627+
derived.get()
628+
})
629+
630+
runInAction(() => {
631+
enabled.set(true)
632+
derived.get()
633+
})
634+
635+
expect(events).toEqual(["BO"])
636+
637+
disposeAutorun()
638+
expect(events).toEqual(["BO", "BUO"])
639+
})
640+
641+
test("temporary computed observers do not activate lifecycle hooks", () => {
642+
const events: string[] = []
643+
const enabled = observable.box(false)
644+
const resource = observable.box(1)
645+
onBecomeObserved(resource, () => events.push("BO"))
646+
onBecomeUnobserved(resource, () => events.push("BUO"))
647+
const inner = computed(() => (enabled.get() ? resource.get() : null))
648+
const outer = computed(() => inner.get())
649+
650+
runInAction(() => {
651+
outer.get()
652+
enabled.set(true)
653+
inner.get()
654+
})
655+
656+
expect(events).toEqual([])
657+
})

packages/mobx/src/core/computedvalue.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
216216
reportObserved(this)
217217
if (shouldCompute(this)) {
218218
let prevTrackingContext = globalState.trackingContext
219-
if (this.keepAlive_ && !prevTrackingContext) {
219+
if (!prevTrackingContext && (this.keepAlive_ || this.isBeingObserved)) {
220+
// An observed or keep-alive computed can be recomputed by an untracked
221+
// read, for example from inside an action. Its dependencies are still
222+
// transitively observed and must fire their lifecycle hooks
220223
globalState.trackingContext = this
221224
}
222225
if (this.trackAndCompute()) {

0 commit comments

Comments
 (0)