@@ -138,14 +138,14 @@ const useAsyncEffect = (generator, options) => {
138
138
const argsToPromiseMap = new Map ( ) ;
139
139
140
140
/**
141
- * @typedef {function } UseAsyncCallbackDecoratedFn
142
- * @param {CPromise } scope
141
+ * @typedef {Function } UseAsyncCallbackDecoratedFn
142
+ * @param {CPromise } [ scope]
143
143
* @property {CancelFn } cancel
144
- * @returns {function| * }
144
+ * @returns {* }
145
145
*/ /**
146
- * @typedef {function } UseAsyncCallbackDecoratedFn
146
+ * @typedef {Function } UseAsyncCallbackDecoratedFn
147
147
* @property {CancelFn } cancel
148
- * @returns {function| * }
148
+ * @returns {* }
149
149
*/
150
150
151
151
/**
@@ -166,7 +166,7 @@ const useAsyncCallback = (generator, options) => {
166
166
throw TypeError ( 'options must be an object or array' ) ;
167
167
}
168
168
169
- const initialState = {
169
+ const initialState = {
170
170
done : false ,
171
171
result : undefined ,
172
172
error : undefined ,
@@ -179,7 +179,8 @@ const useAsyncCallback = (generator, options) => {
179
179
queue : [ ] ,
180
180
pending : 0 ,
181
181
args : null ,
182
- state : initialState
182
+ state : initialState ,
183
+ callback : null
183
184
} ) ;
184
185
185
186
let {
@@ -188,9 +189,9 @@ const useAsyncCallback = (generator, options) => {
188
189
cancelPrevious = false ,
189
190
threads,
190
191
queueSize = - 1 ,
191
- scopeArg= false ,
192
- states= false ,
193
- catchErrors= false
192
+ scopeArg = false ,
193
+ states = false ,
194
+ catchErrors = false
194
195
} = options && Array . isArray ( options ) ? { deps : options } : options || { } ;
195
196
196
197
if ( threads === undefined ) {
@@ -205,28 +206,28 @@ const useAsyncCallback = (generator, options) => {
205
206
}
206
207
}
207
208
208
- if ( queueSize !== - 1 && ( ! Number . isFinite ( queueSize ) || queueSize < - 1 ) ) {
209
+ if ( queueSize !== - 1 && ( ! Number . isFinite ( queueSize ) || queueSize < - 1 ) ) {
209
210
throw Error ( 'queueSize must be a finite number >=-1' ) ;
210
211
}
211
212
212
- const [ state , setState ] = states ? useState ( initialState ) : [ ] ;
213
+ const [ state , setState ] = states ? useState ( initialState ) : [ ] ;
213
214
214
215
const setDeepState = ( newState ) => {
215
216
if ( isEqualObjects ( current . state , newState ) ) return ;
216
217
setState ( newState ) ;
217
218
current . state = newState ;
218
219
}
219
220
220
- const singleThreaded = threads === 1 ;
221
+ const singleThreaded = threads === 1 ;
221
222
222
- const callback = useMemo ( ( ) => {
223
- const cancel = ( reason ) => {
223
+ const callback = useMemo ( ( ) => {
224
+ const cancel = ( reason ) => {
224
225
const _reason = isEvent ( reason ) ? undefined : reason ;
225
226
current . promises . forEach ( promise => promise . cancel ( _reason ) ) ;
226
227
current . promises . length = 0 ;
227
228
} ;
228
229
229
- const fn = ( ...args ) => {
230
+ const fn = ( ...args ) => {
230
231
const { promises, queue} = current ;
231
232
let n ;
232
233
@@ -252,10 +253,10 @@ const useAsyncCallback = (generator, options) => {
252
253
let started ;
253
254
254
255
let promise = new CPromise ( resolve => {
255
- const start = ( ) => {
256
+ const start = ( ) => {
256
257
current . pending ++ ;
257
258
258
- started = true ;
259
+ started = true ;
259
260
260
261
if ( states && singleThreaded ) {
261
262
setDeepState ( {
@@ -277,22 +278,22 @@ const useAsyncCallback = (generator, options) => {
277
278
start ( ) ;
278
279
} ) . weight ( 0 )
279
280
. then ( resolveGenerator )
280
- [ catchErrors ? 'done' : 'finally' ] ( ( value , isRejected ) => {
281
+ [ catchErrors ? 'done' : 'finally' ] ( ( value , isRejected ) => {
281
282
started && current . pending -- ;
282
283
removeElement ( promises , promise ) ;
283
284
combine && argsToPromiseMap . delete ( promise ) ;
284
285
queue . length && queue . shift ( ) ( ) ;
285
- const canceled = ! ! ( isRejected && CanceledError . isCanceledError ( value ) ) ;
286
+ const canceled = ! ! ( isRejected && CanceledError . isCanceledError ( value ) ) ;
286
287
287
- if ( canceled && ( value . code === E_REASON_UNMOUNTED || value . code === E_REASON_RESTART ) ) {
288
+ if ( canceled && ( value . code === E_REASON_UNMOUNTED || value . code === E_REASON_RESTART ) ) {
288
289
return ;
289
290
}
290
291
291
292
states && singleThreaded && setDeepState ( {
292
293
pending : false ,
293
294
done : true ,
294
- error : isRejected ? value : undefined ,
295
- result : isRejected ? undefined : value ,
295
+ error : isRejected ? value : undefined ,
296
+ result : isRejected ? undefined : value ,
296
297
canceled
297
298
} ) ;
298
299
} ) . weight ( 0 ) . aggregate ( ) ;
@@ -304,7 +305,7 @@ const useAsyncCallback = (generator, options) => {
304
305
return promise ;
305
306
}
306
307
307
- const promise = resolveGenerator ( ) [ catchErrors ? 'done' : 'finally' ] ( ( ) => {
308
+ const promise = resolveGenerator ( ) [ catchErrors ? 'done' : 'finally' ] ( ( ) => {
308
309
removeElement ( promises , promise ) ;
309
310
combine && argsToPromiseMap . delete ( promise ) ;
310
311
} ) . weight ( 0 ) . aggregate ( ) ;
@@ -318,7 +319,9 @@ const useAsyncCallback = (generator, options) => {
318
319
return promise ;
319
320
}
320
321
321
- fn . cancel = cancel ;
322
+ fn . cancel = cancel ;
323
+
324
+ current . callback = fn ;
322
325
323
326
return fn ;
324
327
} , deps ) ;
@@ -348,10 +351,74 @@ const useAsyncCallback = (generator, options) => {
348
351
349
352
return callback ;
350
353
}
354
+ /**
355
+ * useAsyncState hook whose setter returns a promise
356
+ * @param {* } [initialValue]
357
+ * @returns {[any, function(*=, boolean=): (Promise<*>|undefined)] }
358
+ */
359
+ const useAsyncState = ( initialValue ) => {
360
+ const [ value , setter ] = useState ( initialValue ) ;
361
+
362
+ return [
363
+ value ,
364
+ /**
365
+ * state async accessor
366
+ * @param {* } [newValue]
367
+ * @returns {Promise<*>|undefined }
368
+ */
369
+ function ( newValue ) {
370
+ return new Promise ( resolve => {
371
+ setter ( ( currentValue ) => {
372
+ if ( arguments . length ) {
373
+ currentValue = typeof newValue === 'function' ? newValue ( currentValue ) : newValue ;
374
+ }
375
+ resolve ( currentValue ) ;
376
+ return currentValue ;
377
+ } )
378
+ } ) ;
379
+ }
380
+ ]
381
+ }
382
+
383
+ const useAsyncWatcher = ( ...value ) => {
384
+ const ref = useRef ( {
385
+ callbacks : [ ] ,
386
+ oldValue : value
387
+ } ) ;
388
+
389
+ const multiple = value . length > 1 ;
390
+
391
+ useEffect ( ( ) => {
392
+ const { current} = ref ;
393
+ const data = multiple ? value . map ( ( value , index ) => [ value , current . oldValue [ index ] ] ) :
394
+ [ value [ 0 ] , current . oldValue [ 0 ] ] ;
395
+ current . callbacks . forEach ( cb => cb ( data ) ) ;
396
+ current . callbacks = [ ] ;
397
+ current . oldValue = value ;
398
+ } , value ) ;
399
+
400
+ /**
401
+ * @param {Boolean } [grabPrevValue]
402
+ * @returns {Promise }
403
+ */
404
+ return ( grabPrevValue ) => {
405
+ return new Promise ( ( resolve ) => {
406
+ ref . current . callbacks . push ( entry => {
407
+ if ( multiple ) {
408
+ resolve ( grabPrevValue ? entry : entry . map ( values => values [ 0 ] ) )
409
+ }
410
+
411
+ resolve ( grabPrevValue ? entry : entry [ 0 ] ) ;
412
+ } ) ;
413
+ } ) ;
414
+ }
415
+ }
351
416
352
417
module . exports = {
353
418
useAsyncEffect,
354
419
useAsyncCallback,
420
+ useAsyncState,
421
+ useAsyncWatcher,
355
422
CanceledError,
356
423
E_REASON_UNMOUNTED ,
357
424
E_REASON_RESTART
0 commit comments