Skip to content
This repository was archived by the owner on Jul 31, 2020. It is now read-only.

Commit 5dca25c

Browse files
committed
feat: add store errors, custom batch size and support return maps
1 parent 2910a1a commit 5dca25c

File tree

2 files changed

+84
-30
lines changed

2 files changed

+84
-30
lines changed

src/createAsyncContainer.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {
2-
types,
3-
IAnyModelType,
4-
isAlive,
52
getParent,
6-
Instance,
73
hasParent,
4+
IAnyModelType,
5+
Instance,
6+
isAlive,
7+
types,
88
} from 'mobx-state-tree';
99
import { now } from 'mobx-utils';
1010
import nextTick from 'next-tick';
@@ -20,8 +20,8 @@ export interface VolatileAsyncContainerState {
2020
export interface AsyncContainerOptions<T> {
2121
ttl?: number;
2222
failstateTtl?: number;
23-
fetch?(id?: string): PromiseLike<Instance<T>>;
2423
name?: string;
24+
fetch?(id?: string): PromiseLike<Instance<T>>;
2525
}
2626

2727
export function createAsyncContainer<T extends IAnyModelType>(

src/createAsyncStore.ts

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,41 @@ import {
1212
import nextTick from 'next-tick';
1313
import { createAsyncContainer } from './createAsyncContainer';
1414

15+
type ReturnValueArray<T> = Array<Instance<T>>;
16+
type ReturnValueMap<T> = Dict<Instance<T> | Error>;
17+
1518
export type AsyncFetchActions<T> = (
1619
self: ModelInstanceType<any, any, any, any>
1720
) => {
1821
fetchOne?(id: string): Promise<Instance<T> | undefined>;
19-
fetchMany?(ids: string[]): Promise<Array<Instance<T>>>;
20-
fetchAll?(): Promise<Array<Instance<T>>>;
22+
fetchMany?(ids: string[]): Promise<ReturnValueArray<T> | ReturnValueMap<T>>;
23+
fetchAll?(): Promise<ReturnValueArray<T> | ReturnValueMap<T>>;
2124
};
2225

2326
export interface VolatileAsyncStoreState {
2427
isPending: boolean;
2528
isReady: boolean;
2629
fetchQueue: string[];
30+
error?: Error;
31+
}
32+
33+
export interface AsyncStoreOptions {
34+
ttl?: number;
35+
failstateTtl?: number;
36+
batch?: number;
2737
}
2838

2939
export function createAsyncStore<T extends IAnyModelType>(
3040
name: string,
3141
ItemModel: T,
3242
fetchActions?: AsyncFetchActions<T>,
33-
options = {
34-
ttl: 0,
35-
failstateTtl: 10000,
36-
}
43+
options?: AsyncStoreOptions
3744
) {
38-
const AsyncContainer = createAsyncContainer<T>(ItemModel, options);
45+
const { ttl = 0, failstateTtl = 10000, batch = 40 } = options || {};
46+
const AsyncContainer = createAsyncContainer<T>(ItemModel, {
47+
ttl,
48+
failstateTtl,
49+
});
3950
type AsyncContainerShape = Instance<typeof AsyncContainer>;
4051

4152
return types
@@ -47,12 +58,25 @@ export function createAsyncStore<T extends IAnyModelType>(
4758
isPending: false,
4859
isReady: false,
4960
}))
61+
.views((self) => ({
62+
get errors(): Dict<Error> {
63+
return values(self.containers).reduce((acc, c) => {
64+
return { ...acc, [c.id]: c.error };
65+
}, {});
66+
},
67+
}))
68+
.views((self) => ({
69+
get inFailstate() {
70+
return Object.keys(self.errors).length > 0;
71+
},
72+
}))
5073
.actions((self) => ({
5174
setReady() {
5275
self.isPending = false;
5376
self.isReady = true;
5477
},
5578
setPending() {
79+
self.error = undefined;
5680
self.isPending = true;
5781
},
5882
hotReload() {
@@ -82,18 +106,30 @@ export function createAsyncStore<T extends IAnyModelType>(
82106
throw new Error("Store doesn't support fetchAll");
83107
}
84108
self.setPending();
85-
const items = yield client.fetchAll();
86-
if (items.length > 0) {
87-
items.forEach((item: any) => {
88-
const ct =
89-
self.containers.get(item.id) ||
90-
AsyncContainer.create({ id: item.id } as any);
91-
const idx = self.fetchQueue.indexOf(item.id);
92-
self.fetchQueue.splice(idx, 1);
93-
ct.setValue(item);
94-
self.containers.set(item.id, ct);
95-
});
109+
let items:
110+
| ReturnValueArray<T>
111+
| ReturnValueMap<T> = yield client.fetchAll();
112+
113+
if (Array.isArray(items)) {
114+
items = items.reduce(
115+
(acc, item) => ({ ...acc, [item.id]: item }),
116+
{}
117+
);
96118
}
119+
120+
Object.keys(items).forEach((id) => {
121+
const ct =
122+
self.containers.get(id) || AsyncContainer.create({ id } as any);
123+
const itemOrError = (items as ReturnValueMap<T>)[id];
124+
const idx = self.fetchQueue.indexOf(id);
125+
self.fetchQueue.splice(idx, 1);
126+
if (itemOrError instanceof Error) {
127+
ct.setFailstate(itemOrError);
128+
} else {
129+
ct.setValue(itemOrError);
130+
}
131+
self.containers.set(id, ct);
132+
});
97133
self.setReady();
98134
}),
99135
_fetchMany: flow(function*(ids: string[]) {
@@ -107,10 +143,27 @@ export function createAsyncStore<T extends IAnyModelType>(
107143
return ct;
108144
});
109145
try {
110-
const items = yield client.fetchMany(ids);
111-
items.forEach((item: Instance<T>) => {
112-
const ct = self.containers.get(item.id)!;
113-
ct.setValue(item);
146+
let items:
147+
| ReturnValueArray<T>
148+
| ReturnValueMap<T> = yield client.fetchMany(ids);
149+
150+
if (Array.isArray(items)) {
151+
items = items.reduce(
152+
(acc, item) => ({ ...acc, [item.id]: item }),
153+
{}
154+
);
155+
}
156+
157+
Object.keys(items).forEach((id) => {
158+
const ct = self.containers.get(id)!;
159+
const itemOrError = (items as ReturnValueMap<T>)[id];
160+
const idx = self.fetchQueue.indexOf(id);
161+
self.fetchQueue.splice(idx, 1);
162+
if (itemOrError instanceof Error) {
163+
ct.setFailstate(itemOrError);
164+
} else {
165+
ct.setValue(itemOrError);
166+
}
114167
});
115168
} catch (e) {
116169
cts.forEach((ct) => {
@@ -141,6 +194,7 @@ export function createAsyncStore<T extends IAnyModelType>(
141194
return AsyncContainer.create({ id } as any);
142195
},
143196
afterCreate() {
197+
const client = fetchActions ? fetchActions(self) : {};
144198
addDisposer(
145199
self,
146200
reaction(
@@ -153,9 +207,9 @@ export function createAsyncStore<T extends IAnyModelType>(
153207
self.spliceFetchQueue(fetchAllIndex, 1);
154208
self._fetchAll();
155209
} else {
156-
// Batch 40 items at a time
157-
const idsToFetch = self.spliceFetchQueue(0, 40);
158-
if (idsToFetch.length === 1) {
210+
// Batch fetching
211+
const idsToFetch = self.spliceFetchQueue(0, batch);
212+
if (idsToFetch.length === 1 && client.fetchOne) {
159213
self._fetchOne(idsToFetch[0]);
160214
} else {
161215
self._fetchMany(idsToFetch);

0 commit comments

Comments
 (0)