Skip to content

Commit 23ffb02

Browse files
committed
Refactor evaluator to cache getters by reference, not hash code
1 parent 10f6f2f commit 23ffb02

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

src/evaluator.js

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
var Immutable = require('immutable')
22
var toImmutable = require('./immutable-helpers').toImmutable
3-
var hashCode = require('./hash-code')
43
var isEqual = require('./is-equal')
54
var getComputeFn = require('./getter').getComputeFn
65
var getDeps = require('./getter').getDeps
@@ -14,8 +13,8 @@ class Evaluator {
1413
constructor() {
1514
/**
1615
* {
17-
* <hashCode>: {
18-
* stateHashCode: number,
16+
* <Getter>: {
17+
* state: Immutable.Map,
1918
* args: Immutable.List,
2019
* value: any,
2120
* }
@@ -46,12 +45,10 @@ class Evaluator {
4645
}
4746

4847
// Must be a Getter
49-
var code = hashCode(keyPathOrGetter)
50-
5148
// if the value is cached for this dispatch cycle, return the cached value
5249
if (this.__isCached(state, keyPathOrGetter)) {
5350
// Cache hit
54-
return this.__cachedGetters.getIn([code, 'value'])
51+
return this.__cachedGetters.getIn([keyPathOrGetter, 'value'])
5552

5653
}
5754

@@ -60,11 +57,11 @@ class Evaluator {
6057

6158
if (this.__hasStaleValue(state, keyPathOrGetter)) {
6259
// getter deps could still be unchanged since we only looked at the unwrapped (keypath, bottom level) deps
63-
var prevArgs = this.__cachedGetters.getIn([code, 'args'])
60+
var prevArgs = this.__cachedGetters.getIn([keyPathOrGetter, 'args'])
6461

6562
// since Getter is a pure functions if the args are the same its a cache hit
6663
if (isEqual(prevArgs, toImmutable(args))) {
67-
var prevValue = this.__cachedGetters.getIn([code, 'value'])
64+
var prevValue = this.__cachedGetters.getIn([keyPathOrGetter, 'value'])
6865
this.__cacheValue(state, keyPathOrGetter, prevArgs, prevValue)
6966
return prevValue
7067
}
@@ -97,11 +94,10 @@ class Evaluator {
9794
* @param {Getter} getter
9895
*/
9996
__hasStaleValue(state, getter) {
100-
var code = hashCode(getter)
10197
var cache = this.__cachedGetters
10298
return (
103-
cache.has(code) &&
104-
cache.getIn([code, 'stateHashCode']) !== state.hashCode()
99+
cache.has(getter) &&
100+
!Immutable.is(cache.getIn([getter, 'state']), state)
105101
)
106102
}
107103

@@ -113,11 +109,10 @@ class Evaluator {
113109
* @param {any} value
114110
*/
115111
__cacheValue(state, getter, args, value) {
116-
var code = hashCode(getter)
117-
this.__cachedGetters = this.__cachedGetters.set(code, Immutable.Map({
112+
this.__cachedGetters = this.__cachedGetters.set(getter, Immutable.Map({
118113
value: value,
119114
args: toImmutable(args),
120-
stateHashCode: state.hashCode(),
115+
state: state,
121116
}))
122117
}
123118

@@ -128,10 +123,9 @@ class Evaluator {
128123
* @return {boolean}
129124
*/
130125
__isCached(state, getter) {
131-
var code = hashCode(getter)
132126
return (
133-
this.__cachedGetters.hasIn([code, 'value']) &&
134-
this.__cachedGetters.getIn([code, 'stateHashCode']) === state.hashCode()
127+
this.__cachedGetters.hasIn([getter, 'value']) &&
128+
Immutable.is(this.__cachedGetters.getIn([getter, 'state']), state)
135129
)
136130
}
137131

test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
var Nuclear = require("./dist/nuclear"); // v1.1.1
2+
3+
var SET_YEAR_GROUP = "SET_YEAR_GROUP";
4+
var LOADED = "LOADED";
5+
6+
function runTest(value) {
7+
8+
var store = Nuclear.Store({
9+
getInitialState: function() {
10+
return Nuclear.toImmutable({
11+
yearGroup: 0,
12+
shouldLoaded: true
13+
});
14+
},
15+
16+
initialize: function() {
17+
this.on(SET_YEAR_GROUP, setYearGroup);
18+
this.on(LOADED, loaded);
19+
}
20+
});
21+
22+
function setYearGroup(store, payload) {
23+
return store
24+
.set("yearGroup", payload.yearGroup)
25+
.set("shouldLoad", true);
26+
}
27+
28+
function loaded(store) {
29+
return store.set("shouldLoad", false);
30+
}
31+
32+
var reactor = new Nuclear.Reactor();
33+
34+
reactor.registerStores({ uiStore: store });
35+
36+
var output = [];
37+
// Record changes to yearGroup
38+
reactor.observe(["uiStore", "yearGroup"], function(y) { output.push(y) });
39+
40+
reactor.dispatch(SET_YEAR_GROUP, {yearGroup: 6});
41+
reactor.dispatch(LOADED);
42+
reactor.dispatch(SET_YEAR_GROUP, {yearGroup: value});
43+
44+
return output;
45+
}
46+
47+
//var output1 = runTest(1);
48+
//console.log(output1); // OK - logs [ 6, 1 ]
49+
50+
var output5 = runTest(5);
51+
console.log(output5); // WRONG! Logs [ 6 ] instead of [ 6, 5 ] !
52+
53+
// Removing the reactor.dispatch("LOADED"); call seems to fix things, but why?!

tests/reactor-tests.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,4 +1144,49 @@ describe('Reactor', () => {
11441144
}).not.toThrow()
11451145
})
11461146
})
1147+
1148+
describe('issue #140 - change observer error case dealing with hashCode collisions', () => {
1149+
it('observer should be called correctly', () => {
1150+
var SET_YEAR_GROUP = 'SET_YEAR_GROUP'
1151+
var LOADED = 'LOADED'
1152+
1153+
var store = Store({
1154+
getInitialState: function() {
1155+
return toImmutable({
1156+
yearGroup: 0,
1157+
shouldLoaded: true,
1158+
})
1159+
},
1160+
1161+
initialize: function() {
1162+
this.on(SET_YEAR_GROUP, setYearGroup)
1163+
this.on(LOADED, loaded)
1164+
},
1165+
})
1166+
1167+
function setYearGroup(store, payload) {
1168+
return store
1169+
.set('yearGroup', payload.yearGroup)
1170+
.set('shouldLoad', true)
1171+
}
1172+
1173+
function loaded(store) {
1174+
return store.set('shouldLoad', false)
1175+
}
1176+
1177+
var reactor = new Reactor()
1178+
1179+
reactor.registerStores({ uiStore: store })
1180+
1181+
var output = []
1182+
// Record changes to yearGroup
1183+
reactor.observe(['uiStore', 'yearGroup'], function(y) { output.push(y) })
1184+
1185+
reactor.dispatch(SET_YEAR_GROUP, {yearGroup: 6})
1186+
reactor.dispatch(LOADED)
1187+
reactor.dispatch(SET_YEAR_GROUP, {yearGroup: 5})
1188+
1189+
expect(output).toEqual([6, 5])
1190+
})
1191+
})
11471192
})

0 commit comments

Comments
 (0)