Skip to content

Commit 8b29696

Browse files
authored
fix(store): improve loose mode invalidation for listing models (#294)
1 parent 1307ad8 commit 8b29696

File tree

4 files changed

+52
-24
lines changed

4 files changed

+52
-24
lines changed

src/cache.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export function assert(target, key, value, force) {
129129
dispatch(entry);
130130
}
131131

132-
export function set(target, key, fn, value) {
132+
export function sync(target, key, fn, value) {
133133
const entry = getEntry(target, key);
134134
const nextValue = fn(target, value, entry.value);
135135

@@ -138,6 +138,9 @@ export function set(target, key, fn, value) {
138138
entry.assertValue = undefined;
139139

140140
dispatch(entry);
141+
142+
// mark as resolved to avoid double fn call in get
143+
entry.resolved = true;
141144
}
142145
}
143146

src/store.js

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function resolveWithInvalidate(config, model, lastModel) {
5151
}
5252

5353
function syncCache(config, id, model, invalidate = true) {
54-
cache.set(config, id, invalidate ? resolveWithInvalidate : resolve, model);
54+
cache.sync(config, id, invalidate ? resolveWithInvalidate : resolve, model);
5555
return model;
5656
}
5757

@@ -1120,33 +1120,19 @@ function get(Model, id) {
11201120
const entry = cache.getEntry(config, stringId);
11211121
const cachedModel = entry.value;
11221122

1123-
if (
1124-
cachedModel &&
1125-
getModelState(cachedModel).state !== "pending" &&
1126-
!validate(cachedModel)
1127-
) {
1123+
if (entry.resolved && cachedModel && !validate(cachedModel)) {
11281124
entry.resolved = false;
11291125
}
11301126

11311127
return cache.get(config, stringId, () => {
11321128
id = normalizeId(id);
11331129

1134-
let validContexts = true;
11351130
if (config.contexts) {
11361131
for (const context of config.contexts) {
1137-
if (
1138-
cache.get(context, context, () => getCurrentTimestamp()) ===
1139-
getCurrentTimestamp()
1140-
) {
1141-
validContexts = false;
1142-
}
1132+
cache.get(context, context, getCurrentTimestamp);
11431133
}
11441134
}
11451135

1146-
if (validContexts && cachedModel && validate(cachedModel)) {
1147-
return cachedModel;
1148-
}
1149-
11501136
const fallback = () =>
11511137
cachedModel ||
11521138
(offline && config.create(offline.get(stringId))) ||
@@ -1734,6 +1720,9 @@ function store(Model, options = {}) {
17341720
...Model,
17351721
[connect]: {
17361722
get(id) {
1723+
const value = cache.getCurrentValue();
1724+
if (value) return value;
1725+
17371726
const model = get(config.model, id);
17381727
return pending(model) || model;
17391728
},

test/spec/cache.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
get,
3-
set,
3+
sync,
44
assert,
55
getEntries,
66
invalidate,
@@ -94,12 +94,12 @@ describe("cache:", () => {
9494
get(target, "one", () => {
9595
get(target, "two", () => "value");
9696
get(target, "three", () => true);
97-
set(target, "three", () => false);
97+
sync(target, "three", () => false);
9898

9999
return "value";
100100
});
101101

102-
set(target, "two", () => "other value");
102+
sync(target, "two", () => "other value");
103103
get(target, "one", spy);
104104
expect(spy).toHaveBeenCalledTimes(1);
105105
});
@@ -123,15 +123,15 @@ describe("cache:", () => {
123123

124124
expect(spy).toHaveBeenCalledTimes(0);
125125

126-
set(target, "key", () => "value");
126+
sync(target, "key", () => "value");
127127
get(target, "key", spy);
128128

129129
expect(spy).toHaveBeenCalledTimes(0);
130130
});
131131

132132
it("invalidates dependant properties", () => {
133133
get(target, "key", () => get(target, "otherKey", () => "value"));
134-
set(target, "otherKey", () => "new value");
134+
sync(target, "otherKey", () => "new value");
135135

136136
get(target, "key", spy);
137137

@@ -170,7 +170,7 @@ describe("cache:", () => {
170170
invalidate(target, "key");
171171
get(target, "key", () => "value");
172172

173-
set(target, "otherKey", () => "new value");
173+
sync(target, "otherKey", () => "new value");
174174
get(target, "key", spy);
175175

176176
expect(spy).toHaveBeenCalledTimes(0);

test/spec/store.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,42 @@ describe("store:", () => {
23232323
expect(args[0]).toEqual(["two"]);
23242324
});
23252325
});
2326+
2327+
it("invalidates listing model when item is changed", async () => {
2328+
storage = {};
2329+
2330+
const ListModel = {
2331+
id: true,
2332+
value: "",
2333+
[store.connect]: {
2334+
get: (id) => storage[id],
2335+
set: (id, values) => {
2336+
storage[values.id] = values;
2337+
return values;
2338+
},
2339+
list: ({ query }) =>
2340+
Object.values(storage).filter(({ value }) => value.includes(query)),
2341+
loose: true,
2342+
},
2343+
};
2344+
2345+
// get empty list
2346+
expect(store.get([ListModel], { query: "test" })).toEqual([]);
2347+
// get another empty list
2348+
expect(store.get([ListModel], { query: "other" })).toEqual([]);
2349+
2350+
// add item matching first query
2351+
await store.set(ListModel, { value: "test" });
2352+
await store.set(ListModel, { value: "other" });
2353+
2354+
// first list is updated
2355+
expect(store.get([ListModel], { query: "other" }).length).toBe(1);
2356+
2357+
await resolveTimeout(() => {
2358+
const list = store.get([ListModel], { query: "test" });
2359+
expect(list[0]?.value).toEqual("test");
2360+
});
2361+
});
23262362
});
23272363

23282364
describe("connected to async storage -", () => {

0 commit comments

Comments
 (0)