Skip to content

Commit 6dc991d

Browse files
committed
fixes equals option with computed boxed values. equals function not invoked in first case, or from/to caught exceptions. added tests.
1 parent 8abf3d1 commit 6dc991d

File tree

4 files changed

+129
-3
lines changed

4 files changed

+129
-3
lines changed

src/api/computed.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export var computed: IComputed = (
6262
const opts: IComputedValueOptions<any> = typeof arg2 === "object" ? arg2 : {};
6363
opts.setter = typeof arg2 === "function" ? arg2 : opts.setter;
6464

65-
const equals = opts.equals || (opts.compareStructural || opts.struct) ? structuralComparer : defaultComparer;
65+
const equals = opts.equals ? opts.equals : (opts.compareStructural || opts.struct) ? structuralComparer : defaultComparer;
6666
return new ComputedValue(arg1, opts.context, equals, opts.name || arg1.name || "", opts.setter);
6767
}
6868
) as any;

src/core/computedvalue.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
4747
lowestObserverState = IDerivationState.UP_TO_DATE;
4848
unboundDepsCount = 0;
4949
__mapid = "#" + getNextId();
50-
protected value: T | undefined | CaughtException = undefined;
50+
protected value: T | undefined | CaughtException = new CaughtException(null);
5151
name: string;
5252
isComputing: boolean = false; // to check for cycles
5353
isRunningSetter: boolean = false;
@@ -138,7 +138,11 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
138138
}
139139
const oldValue = this.value;
140140
const newValue = this.value = this.computeValue(true);
141-
return isCaughtException(newValue) || !this.equals(newValue, oldValue);
141+
return (
142+
isCaughtException(oldValue) ||
143+
isCaughtException(newValue) ||
144+
!this.equals(oldValue, newValue)
145+
);
142146
}
143147

144148
computeValue(track: boolean) {

test/babel/babel-tests.js

+39
Original file line numberDiff line numberDiff line change
@@ -873,3 +873,42 @@ test("action.bound binds (Babel)", t=> {
873873

874874
t.end();
875875
})
876+
877+
test("@computed.equals (Babel)", t => {
878+
const sameTime = (a, b) => {
879+
return (a != null && b != null)
880+
? a.hour === b.hour && a.minute === b.minute
881+
: a === b;
882+
}
883+
class Time {
884+
constructor(hour, minute) {
885+
this.hour = hour;
886+
this.minute = minute;
887+
}
888+
889+
@observable hour: number;
890+
@observable minute: number;
891+
892+
@computed.equals(sameTime) get time() {
893+
return { hour: this.hour, minute: this.minute };
894+
}
895+
}
896+
const time = new Time(9, 0);
897+
898+
const changes = [];
899+
const disposeAutorun = autorun(() => changes.push(time.time));
900+
901+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
902+
time.hour = 9;
903+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
904+
time.minute = 0;
905+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
906+
time.hour = 10;
907+
t.deepEqual(changes, [ { hour: 9, minute: 0 }, { hour: 10, minute: 0 }]);
908+
time.minute = 30;
909+
t.deepEqual(changes, [ { hour: 9, minute: 0 }, { hour: 10, minute: 0 }, { hour: 10, minute: 30 }]);
910+
911+
disposeAutorun();
912+
913+
t.end();
914+
});

test/typescript/typescript-tests.ts

+83
Original file line numberDiff line numberDiff line change
@@ -1120,3 +1120,86 @@ test("803 - action.bound and action preserve type info", t => {
11201120
const bound2 = action.bound(function () {}) as (() => void)
11211121
t.end()
11221122
})
1123+
1124+
test("@computed.equals (TS)", t => {
1125+
const sameTime = (a: Time, b: Time) => {
1126+
return (a != null && b != null)
1127+
? a.hour === b.hour && a.minute === b.minute
1128+
: a === b;
1129+
}
1130+
class Time {
1131+
constructor(hour: number, minute: number) {
1132+
this.hour = hour;
1133+
this.minute = minute;
1134+
}
1135+
1136+
@observable public hour: number;
1137+
@observable public minute: number;
1138+
1139+
@computed.equals(sameTime) public get time() {
1140+
return { hour: this.hour, minute: this.minute };
1141+
}
1142+
}
1143+
const time = new Time(9, 0);
1144+
1145+
const changes: Array<{ hour: number, minute: number }> = [];
1146+
const disposeAutorun = autorun(() => changes.push(time.time));
1147+
1148+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
1149+
time.hour = 9;
1150+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
1151+
time.minute = 0;
1152+
t.deepEqual(changes, [ { hour: 9, minute: 0 }]);
1153+
time.hour = 10;
1154+
t.deepEqual(changes, [ { hour: 9, minute: 0 }, { hour: 10, minute: 0 }]);
1155+
time.minute = 30;
1156+
t.deepEqual(changes, [ { hour: 9, minute: 0 }, { hour: 10, minute: 0 }, { hour: 10, minute: 30 }]);
1157+
1158+
disposeAutorun();
1159+
1160+
t.end();
1161+
});
1162+
1163+
1164+
test("computed equals comparer", t => {
1165+
const comparisons: Array<{ from: string, to: string }> = [];
1166+
const comparer = (from: string, to: string) => {
1167+
comparisons.push({ from, to });
1168+
return from === to;
1169+
};
1170+
1171+
const left = observable("A");
1172+
const right = observable("B");
1173+
const combinedToLowerCase = computed(
1174+
() => left.get().toLowerCase() + right.get().toLowerCase(),
1175+
{ equals: comparer }
1176+
);
1177+
1178+
const values: Array<string> = [];
1179+
const disposeAutorun = autorun(() => values.push(combinedToLowerCase.get()));
1180+
1181+
// No comparison should be made on the first value
1182+
t.deepEqual(comparisons, []);
1183+
1184+
// First change will cause a comparison
1185+
left.set("C");
1186+
t.deepEqual(comparisons, [{ from: "ab", to: "cb" }]);
1187+
1188+
// Transition *to* CaughtException in the computed won't cause a comparison
1189+
left.set(null);
1190+
t.deepEqual(comparisons, [{ from: "ab", to: "cb" }]);
1191+
1192+
// Transition *from* CaughtException in the computed won't cause a comparison
1193+
left.set("D");
1194+
t.deepEqual(comparisons, [{ from: "ab", to: "cb" }]);
1195+
1196+
// Another value change will cause a comparison
1197+
right.set("E");
1198+
t.deepEqual(comparisons, [{ from: "ab", to: "cb" }, { from: "db", to: "de" }]);
1199+
1200+
t.deepEqual(values, ["ab", "cb", "db", "de"]);
1201+
1202+
disposeAutorun();
1203+
1204+
t.end();
1205+
});

0 commit comments

Comments
 (0)