@@ -12,30 +12,41 @@ import {
1212import nextTick from 'next-tick' ;
1313import { createAsyncContainer } from './createAsyncContainer' ;
1414
15+ type ReturnValueArray < T > = Array < Instance < T > > ;
16+ type ReturnValueMap < T > = Dict < Instance < T > | Error > ;
17+
1518export 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
2326export 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
2939export 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