5
5
[ ![ Stars] ( https://badgen.net/github/stars/DigitalBrainJS/use-async-effect )] ( https://github.com/DigitalBrainJS/use-async-effect/stargazers )
6
6
7
7
## useAsyncEffect2 :snowflake :
8
- This library provides ` useAsyncEffect ` and ` useAsyncCallback ` React hooks that make possible to cancel async code
9
- inside it and unsubscribe the internal routines if needed (request, timers etc.).
10
- These hooks are very useful for data fetching, since you don't have to worry about request
11
- cancellation when components unmounts. So just forget about ` isMounted ` flag and ` AbortController ` ,
12
- which were used to protect your components from React leak warning, these hooks do that for you automatically.
13
- Writing cancellable async code becomes easy.
8
+
9
+ This library provides asynchronous versions of the ` useEffect ` and` useCallback ` React hooks that can cancel
10
+ asynchronous code inside it due to timeouts, user requests, or automatically when a component is unmounted or some
11
+ dependency has changed.
12
+
13
+ The library is designed to make it as easy as possible to use complex and composite asynchronous routines
14
+ in React components. It works on top of a [ custom cancellable promise] ( https://www.npmjs.com/package/c-promise2 ) ,
15
+ simplifying the solution to many common challenges with asynchronous code. Can be composed with cancellable version of ` Axios `
16
+ ([ cp-axios] ( https://www.npmjs.com/package/cp-axios ) ) and ` fetch API ` ([ cp-fetch] ( https://www.npmjs.com/package/cp-fetch ) )
17
+ to get auto cancellable React async effects/callbacks with network requests.
18
+
19
+ ### Quick start
20
+
21
+ 1 . You have to use the generator syntax instead of ECMA async functions, basically by replacing ` await ` with ` yield `
22
+ and ` async()=>{} ` or ` async function() ` with ` function* ` :
23
+
24
+ ````javascript
25
+ // plain React useEffect hook
26
+ useEffect(()=>{
27
+ (async()=>{
28
+ await somePromiseHandle;
29
+ })();
30
+ }, [])
31
+ // useAsyncEffect React hook
32
+ useAsyncEffect(function*(){
33
+ yield somePromiseHandle;
34
+ }, [])
35
+ ````
36
+
37
+ 1 . It's recommended to use [ ` CPromise ` ] ( https://www.npmjs.com/package/c-promise2 ) instead of the native Promise to make
38
+ the promise chain deeply cancellable, at least if you're going to change the component state inside it.
39
+
40
+ ````javascript
41
+ import { CPromise } from "c-promise2";
42
+
43
+ const MyComponent= ()=>{
44
+ const [text, setText]= useState('');
45
+
46
+ useAsyncEffect(function*(){
47
+ yield CPromise.delay(1000);
48
+ setText('Hello!');
49
+ });
50
+ }
51
+ ````
52
+
53
+ 1 . Don't catch (or just rethrow caught) ` CanceledError ` errors with ` E_REASON_UNMOUNTED `
54
+ reason inside your code before making any stage change:
55
+
56
+ ````javascript
57
+ import {
58
+ useAsyncEffect,
59
+ E_REASON_UNMOUNTED,
60
+ CanceledError
61
+ } from "use-async-effect2";
62
+ import cpAxios from "cp-axios";
63
+
64
+ const MyComponent= ()=>{
65
+ const [text, setText]= useState('');
66
+
67
+ useAsyncEffect(function*(){
68
+ try{
69
+ const json= (yield cpAxios('http://localhost/')).data;
70
+ setText(`Data: ${JSON.stringify(json)}`);
71
+ }catch(err){
72
+ // just rethrow the CanceledError error if it has E_REASON_UNMOUNTED reason
73
+ CanceledError.rethrow(err, E_REASON_UNMOUNTED);
74
+ // otherwise work with it somehow
75
+ setText(`Failed: ${err.toString}`);
76
+ }
77
+ });
78
+ }
79
+ ````
80
+
14
81
## Installation :hammer :
15
82
- Install for node.js using npm/yarn:
16
83
@@ -37,9 +104,9 @@ When used in conjunction with other libraries from CPromise ecosystem,
37
104
such as [ cp-fetch] ( https://www.npmjs.com/package/cp-fetch ) and [ cp-axios] ( https://www.npmjs.com/package/cp-axios ) ,
38
105
you get a powerful tool for building asynchronous logic of React components.
39
106
40
- ` useAsyncEffect ` & ` useAsyncCallback ` hooks make cancelable async functions from generators,
41
- providing the ability to cancel async sequence in any stage in automatically way, when the related component unmounting.
42
- It's totally the same as async functions, but with cancellation- you need use 'yield' instead of 'await'. That's all.
107
+ ## Examples
108
+
109
+ ### useAsyncEffect
43
110
44
111
A tiny ` useAsyncEffect ` demo with JSON fetching using internal states:
45
112
@@ -84,6 +151,60 @@ function JSONViewer(props) {
84
151
````
85
152
Notice: the related network request will be aborted, when unmounting.
86
153
154
+ An example with a timeout & error handling ([ Live demo] ( https://codesandbox.io/s/async-effect-demo1-vho29?file=/src/TestComponent.js ) ):
155
+ ```` jsx
156
+ import React , { useState } from " react" ;
157
+ import { useAsyncEffect , E_REASON_UNMOUNTED , CanceledError } from " use-async-effect2" ;
158
+ import cpFetch from " cp-fetch" ;
159
+
160
+ export default function TestComponent (props ) {
161
+ const [text , setText ] = useState (" " );
162
+ const [isPending , setIsPending ] = useState (true );
163
+
164
+ const cancel = useAsyncEffect (
165
+ function * ({ onCancel }) {
166
+ console .log (" mount" );
167
+
168
+ this .timeout (props .timeout );
169
+
170
+ onCancel (() => console .log (" scope canceled" ));
171
+
172
+ try {
173
+ setText (" fetching..." );
174
+ const response = yield cpFetch (props .url );
175
+ const json = yield response .json ();
176
+ setIsPending (false );
177
+ setText (` Success: ${ JSON .stringify (json)} ` );
178
+ } catch (err) {
179
+ CanceledError .rethrow (err, E_REASON_UNMOUNTED ); // passthrough for UNMOUNTED rejection
180
+ setIsPending (false );
181
+ setText (` Failed: ${ err} ` );
182
+ }
183
+
184
+ return () => {
185
+ console .log (" unmount" );
186
+ };
187
+ },
188
+ [props .url ]
189
+ );
190
+
191
+ return (
192
+ < div className= " component" >
193
+ < div className= " caption" > useAsyncEffect demo: < / div>
194
+ < div> {text}< / div>
195
+ < button onClick= {cancel} disabled= {! isPending}>
196
+ Cancel request
197
+ < / button>
198
+ < / div>
199
+ );
200
+ }
201
+ ````
202
+
203
+ ### useAsyncCallback
204
+
205
+ Here's a [ Demo App] ( https://codesandbox.io/s/use-async-callback-demo-app-yyic4?file=/src/TestComponent.js ) to play with
206
+ ` asyncCallback ` and learn about its options.
207
+
87
208
Live search for character from the ` rickandmorty ` universe using ` rickandmortyapi.com ` :
88
209
89
210
[ Live demo] ( https://codesandbox.io/s/use-async-effect-axios-rickmorty-search-ui-sd2mv?file=/src/TestComponent.js )
@@ -136,54 +257,6 @@ export default function TestComponent(props) {
136
257
This code handles the cancellation of the previous search sequence (including aborting the request) and
137
258
canceling the sequence when the component is unmounted to avoid the React leak warning.
138
259
139
- An example with a timeout & error handling ([Live demo](https: // codesandbox.io/s/async-effect-demo1-vho29?file=/src/TestComponent.js)):
140
- ` ` ` ` jsx
141
- import React , { useState } from " react" ;
142
- import { useAsyncEffect , E_REASON_UNMOUNTED , CanceledError } from " use-async-effect2" ;
143
- import cpFetch from " cp-fetch" ;
144
-
145
- export default function TestComponent (props ) {
146
- const [text , setText ] = useState (" " );
147
- const [isPending , setIsPending ] = useState (true );
148
-
149
- const cancel = useAsyncEffect (
150
- function * ({ onCancel }) {
151
- console .log (" mount" );
152
-
153
- this .timeout (props .timeout );
154
-
155
- onCancel (() => console .log (" scope canceled" ));
156
-
157
- try {
158
- setText (" fetching..." );
159
- const response = yield cpFetch (props .url );
160
- const json = yield response .json ();
161
- setIsPending (false );
162
- setText (` Success: ${ JSON .stringify (json)} ` );
163
- } catch (err) {
164
- CanceledError .rethrow (err, E_REASON_UNMOUNTED ); // passthrough for UNMOUNTED rejection
165
- setIsPending (false );
166
- setText (` Failed: ${ err} ` );
167
- }
168
-
169
- return () => {
170
- console .log (" unmount" );
171
- };
172
- },
173
- [props .url ]
174
- );
175
-
176
- return (
177
- < div className= " component" >
178
- < div className= " caption" > useAsyncEffect demo: < / div>
179
- < div> {text}< / div>
180
- < button onClick= {cancel} disabled= {! isPending}>
181
- Cancel request
182
- < / button>
183
- < / div>
184
- );
185
- }
186
- ` ` ` `
187
260
` useAsyncCallback` example: fetch with progress capturing & cancellation
188
261
([Live demo](https: // codesandbox.io/s/use-async-callback-axios-catch-ui-l30h5?file=/src/TestComponent.js)):
189
262
` ` ` ` javascript
@@ -293,15 +366,15 @@ Generator context (`this`) and the first argument (if `options.scopeArg` is set)
293
366
- `deps?: any[] | UseAsyncCallbackOptions` - effect dependencies
294
367
#### UseAsyncCallbackOptions:
295
368
- `deps: any[]` - effect dependencies
296
- - `combine:boolean` - subscribe to the result of the async function already
297
- running with the same arguments or run a new one.
369
+ - `combine:boolean` - subscribe to the result of the async function already running with the same arguments instead
370
+ of running a new one.
298
371
- `cancelPrevious:boolean` - cancel the previous pending async function before running a new one.
299
- - `threads: number=0` - set concurrency limit for simultaneous calls. `0` mean unlimited.
372
+ - `threads: number=0` - set concurrency limit for simultaneous calls. `0` means unlimited.
300
373
- `queueSize: number=0` - set max queue size.
301
374
- `scopeArg: boolean=false` - pass `CPromise` scope to the generator function as the first argument.
302
375
- `states: boolean=false` - enable state changing. The function must be single threaded to use the states.
303
376
304
- #### Available states vars:
377
+ #### Available state vars:
305
378
- `pending: boolean` - the function is in the pending state
306
379
- `done: boolean` - the function execution is completed (with success or failure)
307
380
- `result: any` - refers to the resolved function result
0 commit comments