async-fetch 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/package.json +1 -1
- package/useAsyncFetch.js +78 -48
- package/useCache.js +33 -0
package/README.md
CHANGED
|
@@ -56,9 +56,10 @@ The minimum requirement for the hook is a url string as the first argument. The
|
|
|
56
56
|
| initialError | Any | Initial state for the error constant. | |
|
|
57
57
|
| deps | Array | List of dependencies to run the request on. | |
|
|
58
58
|
| poll | Number | Number of milliseconds to wait for polling requests. | |
|
|
59
|
+
| cachetime | Number | Number of milliseconds to cache responses for. | 60000 |
|
|
60
|
+
| timeout | Number | Number of milliseconds to wait before canceling the request. | 30000 |
|
|
59
61
|
| ignoreCleanup | Boolean | Whether or not the hook should cleanup on component unmount. | |
|
|
60
62
|
| ignoreRequest | Boolean | Whether or not the request should send. | |
|
|
61
|
-
| timeout | Number | Number of milliseconds to wait before canceling the request. | 30000 |
|
|
62
63
|
| query | Object | JSON object to append to the url as query params. | |
|
|
63
64
|
| params | Object | JSON object to append to the url as query params. | |
|
|
64
65
|
| data | Object | JSON object to send in the request body. | |
|
|
@@ -77,7 +78,3 @@ The minimum requirement for the hook is a url string as the first argument. The
|
|
|
77
78
|
| data | Any | The response data. |
|
|
78
79
|
| sendRequest | Function | Function to send the request manually. |
|
|
79
80
|
| cancelRequest | Function | Function to cancel the request manually. |
|
|
80
|
-
|
|
81
|
-
### Next.js
|
|
82
|
-
|
|
83
|
-
For the hook to work properly in a Next.js project you must turn `reactStrictMode` to false in your `next.config.js`.
|
package/package.json
CHANGED
package/useAsyncFetch.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useState, useEffect } from "react";
|
|
2
|
+
import useCache from "./useCache.js";
|
|
2
3
|
import useInterval from "./useInterval.js";
|
|
3
4
|
|
|
4
|
-
function useAsyncFetch(
|
|
5
|
+
function useAsyncFetch(path, props = {}) {
|
|
5
6
|
const {
|
|
6
7
|
initialPending,
|
|
7
8
|
initialData,
|
|
8
9
|
initialError,
|
|
9
10
|
deps = [],
|
|
10
11
|
poll,
|
|
12
|
+
cachetime = 60000, // 1 minute.
|
|
13
|
+
timeout = 30000, // 5 minutes.
|
|
11
14
|
ignoreCleanup,
|
|
12
15
|
ignoreRequest,
|
|
13
|
-
timeout = 30000,
|
|
14
16
|
query,
|
|
15
17
|
params,
|
|
16
18
|
data: data2,
|
|
@@ -24,6 +26,8 @@ function useAsyncFetch(url, props = {}) {
|
|
|
24
26
|
|
|
25
27
|
const [pending, setPending] = useState(initialPending);
|
|
26
28
|
|
|
29
|
+
const [pending2, setPending2] = useState();
|
|
30
|
+
|
|
27
31
|
const [data, setData] = useState(initialData);
|
|
28
32
|
|
|
29
33
|
const [error, setError] = useState(initialError);
|
|
@@ -32,19 +36,21 @@ function useAsyncFetch(url, props = {}) {
|
|
|
32
36
|
|
|
33
37
|
const [unmounted, setUnmounted] = useState(false);
|
|
34
38
|
|
|
39
|
+
const cache = useCache(cachetime);
|
|
40
|
+
|
|
35
41
|
useEffect(() => {
|
|
36
42
|
return cleanupRequest;
|
|
37
43
|
}, []);
|
|
38
44
|
|
|
39
45
|
useEffect(() => {
|
|
40
46
|
sendRequest();
|
|
41
|
-
}, [
|
|
47
|
+
}, [path, ...deps]);
|
|
42
48
|
|
|
43
49
|
useInterval(() => {
|
|
44
50
|
sendRequest();
|
|
45
51
|
}, poll);
|
|
46
52
|
|
|
47
|
-
function cancelRequest(
|
|
53
|
+
function cancelRequest() {
|
|
48
54
|
if (cancelSource?.abort) cancelSource.abort();
|
|
49
55
|
}
|
|
50
56
|
|
|
@@ -56,20 +62,28 @@ function useAsyncFetch(url, props = {}) {
|
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
async function sendRequest() {
|
|
59
|
-
if (!
|
|
65
|
+
if (!path) throw new Error("URL is required.");
|
|
66
|
+
|
|
67
|
+
if (typeof path !== "string")
|
|
68
|
+
throw new Error("URL must be of type string.");
|
|
60
69
|
|
|
61
|
-
if (
|
|
70
|
+
if (ignoreRequest !== true) {
|
|
71
|
+
const controller = new AbortController();
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
fetchProps.signal = controller.signal;
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
const requestTimeout = setTimeout(() => {
|
|
76
|
+
controller.abort();
|
|
77
|
+
}, timeout);
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
try {
|
|
80
|
+
let q = "";
|
|
81
|
+
|
|
82
|
+
if (query || params) {
|
|
83
|
+
if (!path.endsWith("?")) q += "?";
|
|
84
|
+
q += new URLSearchParams(query || params).toString();
|
|
85
|
+
}
|
|
70
86
|
|
|
71
|
-
try {
|
|
72
|
-
if (ignoreRequest !== true) {
|
|
73
87
|
const contentType =
|
|
74
88
|
fetchProps.headers?.["Content-Type"] ||
|
|
75
89
|
fetchProps.headers?.["content-type"];
|
|
@@ -80,57 +94,73 @@ function useAsyncFetch(url, props = {}) {
|
|
|
80
94
|
fetchProps.body = JSON.stringify(data2);
|
|
81
95
|
}
|
|
82
96
|
|
|
83
|
-
if (query || params) {
|
|
84
|
-
url += "?" + new URLSearchParams(query || params).toString();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
97
|
if (!unmounted) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
if (pending) {
|
|
99
|
+
setPending2(true);
|
|
100
|
+
} else setPending(true);
|
|
101
|
+
setError();
|
|
91
102
|
cancelRequest();
|
|
92
103
|
setCancelSource(controller);
|
|
104
|
+
if (onStart) onStart();
|
|
93
105
|
}
|
|
94
106
|
|
|
95
|
-
const
|
|
107
|
+
const url = path + q;
|
|
108
|
+
|
|
109
|
+
const cachedResponse = cache.get(url, fetchProps);
|
|
110
|
+
|
|
111
|
+
let parsedResponse = cachedResponse;
|
|
96
112
|
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
JSON.stringify({
|
|
100
|
-
code: response.status,
|
|
101
|
-
text: response.statusText,
|
|
102
|
-
response: await response.text(),
|
|
103
|
-
})
|
|
104
|
-
);
|
|
113
|
+
if (!parsedResponse) {
|
|
114
|
+
const response = await fetch(url, fetchProps);
|
|
105
115
|
|
|
106
|
-
|
|
116
|
+
if (!response.ok)
|
|
117
|
+
throw new Error(
|
|
118
|
+
JSON.stringify({
|
|
119
|
+
code: response.status,
|
|
120
|
+
text: response.statusText,
|
|
121
|
+
response: await response.text(),
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
parsedResponse = await response[parser]();
|
|
126
|
+
}
|
|
107
127
|
|
|
108
128
|
if (!unmounted) {
|
|
109
|
-
setCancelSource();
|
|
110
129
|
setData(parsedResponse);
|
|
111
130
|
if (onSuccess) onSuccess(parsedResponse);
|
|
131
|
+
if (!cachedResponse) cache.set(url, fetchProps, parsedResponse);
|
|
132
|
+
}
|
|
133
|
+
} catch (e) {
|
|
134
|
+
if (!unmounted && e.name !== "AbortError") {
|
|
135
|
+
let error;
|
|
136
|
+
try {
|
|
137
|
+
error = e.toString().replace("Error:", "");
|
|
138
|
+
error = JSON.parse(error.trim());
|
|
139
|
+
} catch {
|
|
140
|
+
error = { response: e.toString(), text: e.toString() };
|
|
141
|
+
}
|
|
142
|
+
setError(error);
|
|
143
|
+
if (onFail) onFail(error);
|
|
144
|
+
}
|
|
145
|
+
} finally {
|
|
146
|
+
clearTimeout(requestTimeout);
|
|
147
|
+
if (!unmounted) {
|
|
148
|
+
if (pending) {
|
|
149
|
+
setPending2();
|
|
150
|
+
} else setPending();
|
|
151
|
+
if (onFinish) onFinish();
|
|
112
152
|
}
|
|
113
|
-
}
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (!unmounted && error.name !== "AbortError") {
|
|
116
|
-
let errorJson;
|
|
117
|
-
try {
|
|
118
|
-
errorJson = error.toString().replace("Error:", "");
|
|
119
|
-
errorJson = JSON.parse(errorJson.trim());
|
|
120
|
-
} catch {}
|
|
121
|
-
setError(errorJson || error);
|
|
122
|
-
if (onFail) onFail(errorJson || error);
|
|
123
|
-
}
|
|
124
|
-
} finally {
|
|
125
|
-
clearTimeout(requestTimeout);
|
|
126
|
-
if (!unmounted) {
|
|
127
|
-
if (setPending) setPending();
|
|
128
|
-
if (onFinish) onFinish();
|
|
129
153
|
}
|
|
130
154
|
}
|
|
131
155
|
}
|
|
132
156
|
|
|
133
|
-
return {
|
|
157
|
+
return {
|
|
158
|
+
pending: pending || pending2,
|
|
159
|
+
data,
|
|
160
|
+
error,
|
|
161
|
+
sendRequest,
|
|
162
|
+
cancelRequest,
|
|
163
|
+
};
|
|
134
164
|
}
|
|
135
165
|
|
|
136
166
|
export default useAsyncFetch;
|
package/useCache.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import useInterval from "./useInterval.js";
|
|
2
|
+
|
|
3
|
+
const cache = {};
|
|
4
|
+
|
|
5
|
+
function useCache(cachetime) {
|
|
6
|
+
useInterval(() => {
|
|
7
|
+
for (let [key, value] of Object.entries(cache))
|
|
8
|
+
if (value.timestamp + cachetime < new Date().getTime()) delete cache[key];
|
|
9
|
+
}, cachetime);
|
|
10
|
+
|
|
11
|
+
function makeKey(url, fetchProps) {
|
|
12
|
+
return typeof fetchProps.body === "string" ? url + fetchProps.body : url;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function get(url, fetchProps) {
|
|
16
|
+
if (cachetime) {
|
|
17
|
+
const key = makeKey(url, fetchProps);
|
|
18
|
+
return cache[key]?.response;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function set(url, fetchProps, response) {
|
|
23
|
+
if (cachetime) {
|
|
24
|
+
const key = makeKey(url, fetchProps);
|
|
25
|
+
const payload = { timestamp: new Date().getTime(), response };
|
|
26
|
+
cache[key] = payload;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { get, set };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default useCache;
|