Skip to content

Commit

Permalink
Change onBecome(Un)Observed implementation to work with multiple list…
Browse files Browse the repository at this point in the history
…eners
  • Loading branch information
michalwarda committed Oct 11, 2018
1 parent 3f161f3 commit 599b4ea
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 11 deletions.
10 changes: 4 additions & 6 deletions src/api/become-observed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@ function interceptHook(hook: "onBecomeObserved" | "onBecomeUnobserved", thing, a
const atom: IObservable =
typeof arg2 === "string" ? getAtom(thing, arg2) : (getAtom(thing) as any)
const cb = typeof arg2 === "string" ? arg3 : arg2
const orig = atom[hook]
const listenersKey = `${hook}Listeners`
atom[listenersKey].add(cb)

const orig = atom[hook]
if (typeof orig !== "function")
return fail(process.env.NODE_ENV !== "production" && "Not an atom that can be (un)observed")

atom[hook] = function() {
orig.call(this)
cb.call(this)
}
return function() {
atom[hook] = orig
atom[listenersKey].delete(cb)
}
}
16 changes: 14 additions & 2 deletions src/core/atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,30 @@ export class Atom implements IAtom {
diffValue = 0
lastAccessedBy = 0
lowestObserverState = IDerivationState.NOT_TRACKING

private observedListeners?: Set<Function>
private unobservedListeners?: Set<Function>

get onBecomeObservedListeners(): Set<Function> {
if (this.observedListeners === undefined) this.observedListeners = new Set()
return this.observedListeners
}
get onBecomeUnobservedListeners(): Set<Function> {
if (this.unobservedListeners === undefined) this.unobservedListeners = new Set()
return this.unobservedListeners
}
/**
* Create a new atom. For debugging purposes it is recommended to give it a name.
* The onBecomeObserved and onBecomeUnobserved callbacks can be used for resource management.
*/
constructor(public name = "Atom@" + getNextId()) {}

public onBecomeUnobserved() {
// noop
this.onBecomeUnobservedListeners.forEach(listener => listener())
}

public onBecomeObserved() {
/* noop */
this.onBecomeObservedListeners.forEach(listener => listener())
}

/**
Expand Down
20 changes: 18 additions & 2 deletions src/core/computedvalue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
private keepAlive: boolean
private firstGet: boolean = true

private observedListeners?: Set<Function>
private unobservedListeners?: Set<Function>

get onBecomeObservedListeners(): Set<Function> {
if (this.observedListeners === undefined) this.observedListeners = new Set()
return this.observedListeners
}
get onBecomeUnobservedListeners(): Set<Function> {
if (this.unobservedListeners === undefined) this.unobservedListeners = new Set()
return this.unobservedListeners
}

/**
* Create a new computed value based on a function expression.
*
Expand Down Expand Up @@ -125,9 +137,13 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
propagateMaybeChanged(this)
}

onBecomeUnobserved() {}
public onBecomeUnobserved() {
this.onBecomeUnobservedListeners.forEach(listener => listener())
}

onBecomeObserved() {}
public onBecomeObserved() {
this.onBecomeObservedListeners.forEach(listener => listener())
}

/**
* Returns the current value of this computed value.
Expand Down
3 changes: 3 additions & 0 deletions src/core/observable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export interface IObservable extends IDepTreeNode {

onBecomeUnobserved(): void
onBecomeObserved(): void

onBecomeUnobservedListeners: Set<Function>
onBecomeObservedListeners: Set<Function>
}

export function hasObservers(observable: IObservable): boolean {
Expand Down
40 changes: 39 additions & 1 deletion test/base/extras.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,6 @@ test("onBecome(Un)Observed - less simple", () => {
x.a = 4

expect(events.length).toBe(0) // nothing happened yet

const d5 = mobx.reaction(() => x.b, () => {})
expect(events.length).toBe(2)
expect(events).toEqual(["b observed", "a observed"])
Expand All @@ -399,6 +398,45 @@ test("onBecome(Un)Observed - less simple", () => {
expect(events.length).toBe(0)
})

test("onBecomeObserved correctly disposes second listener", () => {
const x = mobx.observable.box(3)
const events = []

const d1 = mobx.onBecomeObserved(x, () => {
events.push("x observed")
})
mobx.onBecomeObserved(x, () => {
events.push("x observed 2")
})

d1()
mobx.reaction(() => x.get(), () => {})

expect(events.length).toBe(1)
expect(events).toEqual(["x observed 2"])
})

test("onBecomeUnobserved correctly disposes second listener", () => {
const x = mobx.observable.box(3)
const events = []

const d1 = mobx.onBecomeUnobserved(x, () => {
events.push("x unobserved")
})
mobx.onBecomeUnobserved(x, () => {
events.push("x unobserved 2")
})

d1()

const d3 = mobx.reaction(() => x.get(), () => {})

d3()

expect(events.length).toBe(1)
expect(events).toEqual(["x unobserved 2"])
})

test("deepEquals should yield correct results for complex objects #1118 - 1", () => {
const d2016jan1 = new Date("2016-01-01")
const d2016jan1_2 = new Date("2016-01-01")
Expand Down

0 comments on commit 599b4ea

Please sign in to comment.