From 728a82917afe6361c7dca88e21d5e72dc2118dcc Mon Sep 17 00:00:00 2001 From: Wee Date: Sun, 6 Jan 2019 23:11:18 +0800 Subject: [PATCH] feat: add `equals` option to `observable.box` (resolve #1580) --- src/api/observable.ts | 6 ++-- src/types/observablemap.ts | 9 ++++-- src/types/observableobject.ts | 4 +-- src/types/observablevalue.ts | 13 ++++++-- test/base/observables.js | 58 +++++++++++++++++++++++++++++++++-- 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/api/observable.ts b/src/api/observable.ts index b59ae12b0..2c3cd740c 100644 --- a/src/api/observable.ts +++ b/src/api/observable.ts @@ -1,5 +1,6 @@ import { IEnhancer, + IEqualsComparer, IObservableArray, IObservableDecorator, IObservableMapInitialValues, @@ -25,6 +26,7 @@ import { export type CreateObservableOptions = { name?: string + equals?: IEqualsComparer deep?: boolean defaultDecorator?: IObservableDecorator proxy?: boolean @@ -41,7 +43,7 @@ export const defaultCreateObservableOptions: CreateObservableOptions = { Object.freeze(defaultCreateObservableOptions) function assertValidOption(key: string) { - if (!/^(deep|name|defaultDecorator|proxy)$/.test(key)) + if (!/^(deep|name|equals|defaultDecorator|proxy)$/.test(key)) fail(`invalid option for (extend)observable: ${key}`) } @@ -142,7 +144,7 @@ const observableFactories: IObservableFactories = { box(value?: T, options?: CreateObservableOptions): IObservableValue { if (arguments.length > 2) incorrectlyUsedAsDecorator("box") const o = asCreateObservableOptions(options) - return new ObservableValue(value, getEnhancerFromOptions(o), o.name) + return new ObservableValue(value, getEnhancerFromOptions(o), o) }, array(initialValues?: T[], options?: CreateObservableOptions): IObservableArray { if (arguments.length > 2) incorrectlyUsedAsDecorator("array") diff --git a/src/types/observablemap.ts b/src/types/observablemap.ts index eeb61382c..ac166fedd 100644 --- a/src/types/observablemap.ts +++ b/src/types/observablemap.ts @@ -174,7 +174,12 @@ export class ObservableMap if (entry) { entry.setNewValue(value) } else { - entry = new ObservableValue(value, referenceEnhancer, `${this.name}.${key}?`, false) + entry = new ObservableValue( + value, + referenceEnhancer, + { name: `${this.name}.${key}?` }, + false + ) this._hasMap.set(key, entry) } return entry @@ -210,7 +215,7 @@ export class ObservableMap const observable = new ObservableValue( newValue, this.enhancer, - `${this.name}.${key}`, + { name: `${this.name}.${key}` }, false ) this._data.set(key, observable) diff --git a/src/types/observableobject.ts b/src/types/observableobject.ts index b1f28c8eb..ee8f1579d 100644 --- a/src/types/observableobject.ts +++ b/src/types/observableobject.ts @@ -148,7 +148,7 @@ export class ObservableObjectAdministration entry = new ObservableValue( exists, referenceEnhancer, - `${this.name}.${key.toString()}?`, + { name: `${this.name}.${key.toString()}?` }, false ) map.set(key, entry) @@ -173,7 +173,7 @@ export class ObservableObjectAdministration const observable = new ObservableValue( newValue, enhancer, - `${this.name}.${propName}`, + { name: `${this.name}.${propName}` }, false ) this.values.set(propName, observable) diff --git a/src/types/observablevalue.ts b/src/types/observablevalue.ts index 8436c75db..823d75e1c 100644 --- a/src/types/observablevalue.ts +++ b/src/types/observablevalue.ts @@ -2,10 +2,12 @@ import { Atom, IEnhancer, IInterceptable, + IEqualsComparer, IInterceptor, IListenable, Lambda, checkIfStateModificationsAreAllowed, + comparer, createInstanceofPredicate, getNextId, hasInterceptors, @@ -47,11 +49,15 @@ export class ObservableValue extends Atom changeListeners value dehancer: any + private equals: IEqualsComparer constructor( value: T, public enhancer: IEnhancer, - name = "ObservableValue@" + getNextId(), + { + name = "ObservableValue@" + getNextId(), + equals = comparer.default + }: { name?: string; equals?: IEqualsComparer }, notifySpy = true ) { super(name) @@ -60,6 +66,7 @@ export class ObservableValue extends Atom // only notify spy if this is a stand-alone observable spyReport({ type: "create", name: this.name, newValue: "" + this.value }) } + this.equals = equals } private dehanceValue(value: T): T { @@ -70,7 +77,9 @@ export class ObservableValue extends Atom public set(newValue: T) { const oldValue = this.value newValue = this.prepareNewValue(newValue) as any - if (newValue !== globalState.UNCHANGED) { + const changed = !this.equals(oldValue, newValue) + + if (newValue !== globalState.UNCHANGED && changed) { const notifySpy = isSpyEnabled() if (notifySpy && process.env.NODE_ENV !== "production") { spyReportStart({ diff --git a/test/base/observables.js b/test/base/observables.js index e2f78d4af..62e932e6b 100644 --- a/test/base/observables.js +++ b/test/base/observables.js @@ -149,6 +149,60 @@ test("dynamic2", function(done) { } }) +test("box uses equals", function(done) { + try { + var x = observable.box("a", { + equals: (oldValue, newValue) => { + return oldValue.toLowerCase() === newValue.toLowerCase() + } + }) + + var b = buffer() + m.observe(x, b) + + x.set("A") + x.set("b") + x.set("B") + x.set("C") + + expect(["b", "C"]).toEqual(b.toArray()) + expect(mobx._isComputingDerivation()).toBe(false) + + done() + } catch (e) { + console.log(e.stack) + } +}) + +test("box uses equals2", function(done) { + try { + var x = observable.box("01", { + equals: (oldValue, newValue) => { + return parseInt(oldValue) === parseInt(newValue) + } + }) + + var y = computed(function() { + return parseInt(x) + }) + + var b = buffer() + m.observe(y, b) + + x.set("2") + x.set("02") + x.set("002") + x.set("03") + + expect([2, 3]).toEqual(b.toArray()) + expect(mobx._isComputingDerivation()).toBe(false) + + done() + } catch (e) { + console.log(e.stack) + } +}) + test("readme1", function(done) { try { var b = buffer() @@ -157,7 +211,7 @@ test("readme1", function(done) { var order = {} order.price = observable.box(10) // Prints: New price: 24 - //in TS, just: value(() => this.price() * (1+vat())) + // in TS, just: value(() => this.price() * (1+vat())) order.priceWithVat = computed(function() { return order.price.get() * (1 + vat.get()) }) @@ -191,7 +245,7 @@ test("batch", function() { a.set(4) b.set(5) - // Note, 60 should not happen! (that is d beign computed before c after update of b) + // Note, 60 should not happen! (that is d begin computed before c after update of b) expect(buf.toArray()).toEqual([36, 100]) var x = mobx.transaction(function() {