async-fetch 0.3.7 → 0.3.9
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/LICENSE +21 -0
- package/README.md +84 -46
- package/dist/cjs/index.js +145 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/interfaces.js +3 -0
- package/dist/cjs/interfaces.js.map +1 -0
- package/dist/cjs/normalizeError.js +36 -0
- package/dist/cjs/normalizeError.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types/index.d.ts +35 -0
- package/dist/cjs/types/index.d.ts.map +1 -0
- package/dist/cjs/types/interfaces.d.ts +31 -0
- package/dist/cjs/types/interfaces.d.ts.map +1 -0
- package/dist/cjs/types/normalizeError.d.ts +14 -0
- package/dist/cjs/types/normalizeError.d.ts.map +1 -0
- package/dist/cjs/types/useInterval.d.ts +12 -0
- package/dist/cjs/types/useInterval.d.ts.map +1 -0
- package/dist/cjs/useInterval.js +26 -0
- package/dist/cjs/useInterval.js.map +1 -0
- package/dist/esm/index.js +140 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/interfaces.js +2 -0
- package/dist/esm/interfaces.js.map +1 -0
- package/dist/esm/normalizeError.js +34 -0
- package/dist/esm/normalizeError.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/types/index.d.ts +35 -0
- package/dist/esm/types/index.d.ts.map +1 -0
- package/dist/esm/types/interfaces.d.ts +31 -0
- package/dist/esm/types/interfaces.d.ts.map +1 -0
- package/dist/esm/types/normalizeError.d.ts +14 -0
- package/dist/esm/types/normalizeError.d.ts.map +1 -0
- package/dist/esm/types/useInterval.d.ts +12 -0
- package/dist/esm/types/useInterval.d.ts.map +1 -0
- package/dist/esm/useInterval.js +24 -0
- package/dist/esm/useInterval.js.map +1 -0
- package/package.json +59 -24
- package/dist/index.d.ts +0 -30
- package/dist/index.js +0 -117
- package/dist/useInterval.d.ts +0 -2
- package/dist/useInterval.js +0 -17
- package/tsconfig.json +0 -112
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
import useInterval from "./useInterval.js";
|
|
3
|
+
import normalizeError from "./normalizeError.js";
|
|
4
|
+
/**
|
|
5
|
+
* A React hook for managing async fetch requests with built-in state, cancellation, and polling.
|
|
6
|
+
* @example
|
|
7
|
+
* // Auto-fetch on mount:
|
|
8
|
+
* const { pending, error, data } = useAsyncFetch<User>("/api/user/1");
|
|
9
|
+
*
|
|
10
|
+
* // Manual trigger:
|
|
11
|
+
* const { pending, error, data, sendRequest } = useAsyncFetch<User>("/api/user/1", { auto: false });
|
|
12
|
+
* <button onClick={sendRequest}>Fetch</button>
|
|
13
|
+
*
|
|
14
|
+
* // With polling:
|
|
15
|
+
* const { data } = useAsyncFetch<Status>("/api/status", { poll: 5000 });
|
|
16
|
+
*
|
|
17
|
+
* // POST with JSON body:
|
|
18
|
+
* const { pending, error, data } = useAsyncFetch<Response>("/api/submit", {
|
|
19
|
+
* method: "POST",
|
|
20
|
+
* data: { name: "foo" },
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // With callbacks:
|
|
24
|
+
* useAsyncFetch<User>("/api/user/1", {
|
|
25
|
+
* onStart: () => console.log("started"),
|
|
26
|
+
* onSuccess: (data) => console.log(data),
|
|
27
|
+
* onFail: (error) => console.error(error),
|
|
28
|
+
* onFinish: () => console.log("finished"),
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Cancel on demand:
|
|
32
|
+
* const { cancelRequest } = useAsyncFetch<User>("/api/user/1");
|
|
33
|
+
* <button onClick={cancelRequest}>Cancel</button>
|
|
34
|
+
*/
|
|
35
|
+
function useAsyncFetch(urlString, props = {}) {
|
|
36
|
+
const { initialPending = false, initialError, initialData, auto = true, poll, timeout = 30000, ignoreRequest, ignoreCleanup, query, params, data: body, parser = "json", onStart, onSuccess, onFail, onFinish, ...fetchOptions } = props;
|
|
37
|
+
const [pending, setPending] = useState(initialPending);
|
|
38
|
+
const [error, setError] = useState(initialError);
|
|
39
|
+
const [data, setData] = useState(initialData);
|
|
40
|
+
const fetchOptionsRef = useRef(fetchOptions);
|
|
41
|
+
const controllerRef = useRef(null);
|
|
42
|
+
const requestIdRef = useRef(0);
|
|
43
|
+
fetchOptionsRef.current = fetchOptions;
|
|
44
|
+
const cancelRequest = useCallback(() => {
|
|
45
|
+
controllerRef.current?.abort();
|
|
46
|
+
controllerRef.current = null;
|
|
47
|
+
}, []);
|
|
48
|
+
const sendRequest = useCallback(async () => {
|
|
49
|
+
if (ignoreRequest === true)
|
|
50
|
+
return;
|
|
51
|
+
cancelRequest();
|
|
52
|
+
setPending(true);
|
|
53
|
+
setError(undefined);
|
|
54
|
+
if (onStart)
|
|
55
|
+
onStart();
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
controllerRef.current = controller;
|
|
58
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
59
|
+
const requestId = ++requestIdRef.current;
|
|
60
|
+
try {
|
|
61
|
+
const url = new URL(urlString, window.location.origin);
|
|
62
|
+
if (query ?? params) {
|
|
63
|
+
url.search = new URLSearchParams({ ...query, ...params }).toString();
|
|
64
|
+
}
|
|
65
|
+
const headers = new Headers(fetchOptionsRef.current.headers);
|
|
66
|
+
let resolvedBody;
|
|
67
|
+
if (headers.get("content-type") === "application/x-www-form-urlencoded") {
|
|
68
|
+
resolvedBody = new URLSearchParams(body ?? {});
|
|
69
|
+
}
|
|
70
|
+
else if (body instanceof FormData || body instanceof Blob) {
|
|
71
|
+
resolvedBody = body;
|
|
72
|
+
}
|
|
73
|
+
else if (body !== undefined) {
|
|
74
|
+
resolvedBody = JSON.stringify(body);
|
|
75
|
+
}
|
|
76
|
+
const response = await fetch(url, {
|
|
77
|
+
...fetchOptionsRef.current,
|
|
78
|
+
headers,
|
|
79
|
+
body: resolvedBody ?? fetchOptionsRef.current.body,
|
|
80
|
+
signal: controller.signal,
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw Object.assign(new Error(response.statusText), {
|
|
84
|
+
status: response.status,
|
|
85
|
+
statusText: response.statusText,
|
|
86
|
+
response: await response.text(),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const parsedResponse = (await response[parser]());
|
|
90
|
+
setData(parsedResponse);
|
|
91
|
+
if (onSuccess)
|
|
92
|
+
onSuccess(parsedResponse);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (err?.name !== "AbortError") {
|
|
96
|
+
const normalizedError = normalizeError(err);
|
|
97
|
+
setError(normalizedError);
|
|
98
|
+
if (onFail)
|
|
99
|
+
onFail(normalizedError);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
clearTimeout(timeoutId);
|
|
104
|
+
if (requestId === requestIdRef.current) {
|
|
105
|
+
setPending(false);
|
|
106
|
+
if (onFinish)
|
|
107
|
+
onFinish();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}, [
|
|
111
|
+
ignoreRequest,
|
|
112
|
+
cancelRequest,
|
|
113
|
+
timeout,
|
|
114
|
+
urlString,
|
|
115
|
+
query,
|
|
116
|
+
params,
|
|
117
|
+
body,
|
|
118
|
+
parser,
|
|
119
|
+
onStart,
|
|
120
|
+
onSuccess,
|
|
121
|
+
onFail,
|
|
122
|
+
onFinish,
|
|
123
|
+
]);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (auto === true)
|
|
126
|
+
void sendRequest();
|
|
127
|
+
}, [auto, sendRequest]);
|
|
128
|
+
useInterval(() => {
|
|
129
|
+
void sendRequest();
|
|
130
|
+
}, poll);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
return () => {
|
|
133
|
+
if (ignoreCleanup !== true)
|
|
134
|
+
cancelRequest();
|
|
135
|
+
};
|
|
136
|
+
}, [ignoreCleanup, cancelRequest]);
|
|
137
|
+
return { pending, error, data, cancelRequest, sendRequest };
|
|
138
|
+
}
|
|
139
|
+
export default useAsyncFetch;
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/interfaces.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes an unknown thrown value into a `FetchError`-shaped object.
|
|
3
|
+
* Handles `Error` instances, error-like objects, and primitive values.
|
|
4
|
+
* @example
|
|
5
|
+
* normalizeError(new Error("oops")) // { status: 500, statusText: "oops", response: "<stack>" }
|
|
6
|
+
* normalizeError({ status: 404, statusText: "Not Found", response: "" }) // { status: 404, statusText: "Not Found", response: "" }
|
|
7
|
+
* normalizeError({ status: 403 }) // { status: 403, statusText: "", response: "" }
|
|
8
|
+
* normalizeError("something went wrong") // { status: 500, statusText: "something went wrong", response: "" }
|
|
9
|
+
* normalizeError(null) // { status: 500, statusText: "null", response: "" }
|
|
10
|
+
*/
|
|
11
|
+
function normalizeError(err) {
|
|
12
|
+
if (err instanceof Error) {
|
|
13
|
+
return {
|
|
14
|
+
status: 500,
|
|
15
|
+
statusText: err.message,
|
|
16
|
+
response: err.stack ?? "",
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (typeof err === "object" && err !== null) {
|
|
20
|
+
const e = err;
|
|
21
|
+
return {
|
|
22
|
+
status: e.status ?? 500,
|
|
23
|
+
statusText: e.statusText ?? "",
|
|
24
|
+
response: e.response ?? "",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
status: 500,
|
|
29
|
+
statusText: String(err),
|
|
30
|
+
response: "",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export default normalizeError;
|
|
34
|
+
//# sourceMappingURL=normalizeError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeError.js","sourceRoot":"","sources":["../../src/normalizeError.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAiB,GAAY;IAClD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,OAAO;YACL,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,GAAG,CAAC,OAAO;YACvB,QAAQ,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;SACrB,CAAC;IACT,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,CAAC,GAAG,GAA0B,CAAC;QACrC,OAAO;YACL,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,GAAG;YACvB,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,EAAE;YAC9B,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE;SACtB,CAAC;IACT,CAAC;IAED,OAAO;QACL,MAAM,EAAE,GAAG;QACX,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC;QACvB,QAAQ,EAAE,EAAE;KACR,CAAC;AACT,CAAC;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type FetchError, type RequestProps, type ResponseProps } from "./interfaces.js";
|
|
2
|
+
/**
|
|
3
|
+
* A React hook for managing async fetch requests with built-in state, cancellation, and polling.
|
|
4
|
+
* @example
|
|
5
|
+
* // Auto-fetch on mount:
|
|
6
|
+
* const { pending, error, data } = useAsyncFetch<User>("/api/user/1");
|
|
7
|
+
*
|
|
8
|
+
* // Manual trigger:
|
|
9
|
+
* const { pending, error, data, sendRequest } = useAsyncFetch<User>("/api/user/1", { auto: false });
|
|
10
|
+
* <button onClick={sendRequest}>Fetch</button>
|
|
11
|
+
*
|
|
12
|
+
* // With polling:
|
|
13
|
+
* const { data } = useAsyncFetch<Status>("/api/status", { poll: 5000 });
|
|
14
|
+
*
|
|
15
|
+
* // POST with JSON body:
|
|
16
|
+
* const { pending, error, data } = useAsyncFetch<Response>("/api/submit", {
|
|
17
|
+
* method: "POST",
|
|
18
|
+
* data: { name: "foo" },
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // With callbacks:
|
|
22
|
+
* useAsyncFetch<User>("/api/user/1", {
|
|
23
|
+
* onStart: () => console.log("started"),
|
|
24
|
+
* onSuccess: (data) => console.log(data),
|
|
25
|
+
* onFail: (error) => console.error(error),
|
|
26
|
+
* onFinish: () => console.log("finished"),
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Cancel on demand:
|
|
30
|
+
* const { cancelRequest } = useAsyncFetch<User>("/api/user/1");
|
|
31
|
+
* <button onClick={cancelRequest}>Cancel</button>
|
|
32
|
+
*/
|
|
33
|
+
declare function useAsyncFetch<T = unknown, E = FetchError>(urlString: string, props?: RequestProps<T, E>): ResponseProps<T, E>;
|
|
34
|
+
export default useAsyncFetch;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAKzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,iBAAS,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,UAAU,EAChD,SAAS,EAAE,MAAM,EACjB,KAAK,GAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAM,GAC7B,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CA+IrB;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface FetchError {
|
|
2
|
+
status: number;
|
|
3
|
+
statusText: string;
|
|
4
|
+
response: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RequestProps<T, E> extends RequestInit {
|
|
7
|
+
initialPending?: boolean;
|
|
8
|
+
initialError?: E;
|
|
9
|
+
initialData?: T;
|
|
10
|
+
auto?: boolean;
|
|
11
|
+
poll?: number;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
ignoreRequest?: boolean;
|
|
14
|
+
ignoreCleanup?: boolean;
|
|
15
|
+
query?: Record<string, string>;
|
|
16
|
+
params?: Record<string, string>;
|
|
17
|
+
data?: unknown;
|
|
18
|
+
parser?: "json" | "text" | "blob" | "formData" | "arrayBuffer";
|
|
19
|
+
onStart?: () => void;
|
|
20
|
+
onSuccess?: (data: T) => void;
|
|
21
|
+
onFail?: (error: E) => void;
|
|
22
|
+
onFinish?: () => void;
|
|
23
|
+
}
|
|
24
|
+
export interface ResponseProps<T, E> {
|
|
25
|
+
pending: boolean;
|
|
26
|
+
error?: E;
|
|
27
|
+
data?: T;
|
|
28
|
+
sendRequest: () => Promise<void>;
|
|
29
|
+
cancelRequest: () => void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=interfaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../src/interfaces.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,WAAW;IACrD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,WAAW,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;IAC/D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type FetchError } from "./interfaces.js";
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes an unknown thrown value into a `FetchError`-shaped object.
|
|
4
|
+
* Handles `Error` instances, error-like objects, and primitive values.
|
|
5
|
+
* @example
|
|
6
|
+
* normalizeError(new Error("oops")) // { status: 500, statusText: "oops", response: "<stack>" }
|
|
7
|
+
* normalizeError({ status: 404, statusText: "Not Found", response: "" }) // { status: 404, statusText: "Not Found", response: "" }
|
|
8
|
+
* normalizeError({ status: 403 }) // { status: 403, statusText: "", response: "" }
|
|
9
|
+
* normalizeError("something went wrong") // { status: 500, statusText: "something went wrong", response: "" }
|
|
10
|
+
* normalizeError(null) // { status: 500, statusText: "null", response: "" }
|
|
11
|
+
*/
|
|
12
|
+
declare function normalizeError<E = FetchError>(err: unknown): E;
|
|
13
|
+
export default normalizeError;
|
|
14
|
+
//# sourceMappingURL=normalizeError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizeError.d.ts","sourceRoot":"","sources":["../../../src/normalizeError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;;;GASG;AACH,iBAAS,cAAc,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,CAAC,CAuBvD;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets up a polling interval that calls a callback on a given delay.
|
|
3
|
+
* The callback ref is kept up to date so stale closures are avoided.
|
|
4
|
+
* No interval is created if `poll` is not a valid integer.
|
|
5
|
+
* @see https://github.com/Hermanya/use-interval/blob/master/src/index.tsx
|
|
6
|
+
* @example
|
|
7
|
+
* useInterval(() => fetchData(), 5000) // calls fetchData every 5 seconds
|
|
8
|
+
* useInterval(() => fetchData()) // no interval created
|
|
9
|
+
*/
|
|
10
|
+
declare function useInterval(callback: () => void, poll?: number): void;
|
|
11
|
+
export default useInterval;
|
|
12
|
+
//# sourceMappingURL=useInterval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInterval.d.ts","sourceRoot":"","sources":["../../../src/useInterval.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,iBAAS,WAAW,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAc9D;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useRef, useEffect } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Sets up a polling interval that calls a callback on a given delay.
|
|
4
|
+
* The callback ref is kept up to date so stale closures are avoided.
|
|
5
|
+
* No interval is created if `poll` is not a valid integer.
|
|
6
|
+
* @see https://github.com/Hermanya/use-interval/blob/master/src/index.tsx
|
|
7
|
+
* @example
|
|
8
|
+
* useInterval(() => fetchData(), 5000) // calls fetchData every 5 seconds
|
|
9
|
+
* useInterval(() => fetchData()) // no interval created
|
|
10
|
+
*/
|
|
11
|
+
function useInterval(callback, poll) {
|
|
12
|
+
const callbackRef = useRef(callback);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
callbackRef.current = callback;
|
|
15
|
+
}, [callback]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!Number.isInteger(poll))
|
|
18
|
+
return;
|
|
19
|
+
const id = setInterval(() => callbackRef.current(), poll);
|
|
20
|
+
return () => clearInterval(id);
|
|
21
|
+
}, [poll]);
|
|
22
|
+
}
|
|
23
|
+
export default useInterval;
|
|
24
|
+
//# sourceMappingURL=useInterval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInterval.js","sourceRoot":"","sources":["../../src/useInterval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1C;;;;;;;;GAQG;AACH,SAAS,WAAW,CAAC,QAAoB,EAAE,IAAa;IACtD,MAAM,WAAW,GAAG,MAAM,CAAa,QAAQ,CAAC,CAAC;IAEjD,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;YAAE,OAAO;QAEpC,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAE1D,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACb,CAAC;AAED,eAAe,WAAW,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,35 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "async-fetch",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsc",
|
|
10
|
-
"test-esm": "yarn build && node test/test-esm.mjs"
|
|
11
|
-
},
|
|
3
|
+
"packageManager": "yarn@4.13.0",
|
|
4
|
+
"version": "0.3.9",
|
|
5
|
+
"description": "A React hook for async fetch requests.",
|
|
6
|
+
"author": "Nameer Rizvi (https://github.com/nameer-rizvi)",
|
|
7
|
+
"license": "MIT",
|
|
12
8
|
"repository": {
|
|
13
9
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/nameer-rizvi/
|
|
10
|
+
"url": "git+https://github.com/nameer-rizvi/async-fetch.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/nameer-rizvi/async-fetch#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/nameer-rizvi/async-fetch/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
17
|
+
"typescript",
|
|
18
|
+
"utils",
|
|
19
|
+
"utilities",
|
|
20
|
+
"esm",
|
|
21
|
+
"cjs"
|
|
21
22
|
],
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"main": "dist/cjs/index.js",
|
|
28
|
+
"module": "dist/esm/index.js",
|
|
29
|
+
"types": "dist/cjs/types/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "dist/esm/types/index.d.ts",
|
|
34
|
+
"default": "dist/esm/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "dist/cjs/types/index.d.ts",
|
|
38
|
+
"default": "dist/cjs/index.js"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"update": "yarn && node scripts/updateDeps.js",
|
|
47
|
+
"clean": "rimraf dist",
|
|
48
|
+
"lint": "eslint src/**/*",
|
|
49
|
+
"lint:fix": "eslint src/**/* --fix",
|
|
50
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
51
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
52
|
+
"build": "yarn clean && yarn lint && yarn build:cjs && yarn build:esm && node scripts/postbuild.mjs && echo '✅ Build complete! CJS and ESM outputs ready in dist/'",
|
|
53
|
+
"test:cjs": "node tests/test.cjs",
|
|
54
|
+
"test:esm": "node tests/test.mjs",
|
|
55
|
+
"test": "yarn build && yarn test:cjs && yarn test:esm",
|
|
56
|
+
"prepublishOnly": "yarn build"
|
|
26
57
|
},
|
|
27
|
-
"homepage": "https://github.com/nameer-rizvi/useAsyncFetch#readme",
|
|
28
58
|
"devDependencies": {
|
|
29
|
-
"@
|
|
30
|
-
"@types/react": "^
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
59
|
+
"@eslint/js": "^10.0.1",
|
|
60
|
+
"@types/react": "^19.2.14",
|
|
61
|
+
"eslint": "^10.2.0",
|
|
62
|
+
"react": "^19.2.5",
|
|
63
|
+
"rimraf": "^6.1.3",
|
|
64
|
+
"typescript": "^6.0.2",
|
|
65
|
+
"typescript-eslint": "^8.58.0"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"react": ">=16"
|
|
34
69
|
}
|
|
35
70
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
interface RequestProps {
|
|
2
|
-
initialPending?: boolean;
|
|
3
|
-
initialData?: any;
|
|
4
|
-
initialError?: any;
|
|
5
|
-
deps?: string[];
|
|
6
|
-
poll?: number;
|
|
7
|
-
timeout?: number;
|
|
8
|
-
ignoreRequest?: boolean;
|
|
9
|
-
ignoreCleanup?: boolean;
|
|
10
|
-
query?: any;
|
|
11
|
-
params?: any;
|
|
12
|
-
data?: any;
|
|
13
|
-
parser?: "json" | "text" | "blob" | "formData" | "arrayBuffer";
|
|
14
|
-
onStart?: () => void;
|
|
15
|
-
onSuccess?: (data: any) => void;
|
|
16
|
-
onFail?: (error: any) => void;
|
|
17
|
-
onFinish?: () => void;
|
|
18
|
-
headers?: Record<string, string>;
|
|
19
|
-
body?: any;
|
|
20
|
-
signal?: AbortSignal;
|
|
21
|
-
}
|
|
22
|
-
interface ResponseProps {
|
|
23
|
-
pending?: boolean;
|
|
24
|
-
data?: any;
|
|
25
|
-
error?: any;
|
|
26
|
-
sendRequest: () => void;
|
|
27
|
-
cancelRequest: () => void;
|
|
28
|
-
}
|
|
29
|
-
declare function useAsyncFetch(stringUrl: string, props?: RequestProps): ResponseProps;
|
|
30
|
-
export default useAsyncFetch;
|
package/dist/index.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useEffect } from "react";
|
|
2
|
-
import useInterval from "./useInterval.js";
|
|
3
|
-
function useAsyncFetch(stringUrl, props = {}) {
|
|
4
|
-
const { initialPending, initialData, initialError, deps = [], poll, timeout = 30000, // 30 seconds.
|
|
5
|
-
ignoreRequest, ignoreCleanup, query, params, data: body, parser = "json", onStart, onSuccess, onFail, onFinish, ...fetchProps } = props;
|
|
6
|
-
const [pending, setPending] = useState(initialPending);
|
|
7
|
-
const [pending2, setPending2] = useState(initialPending);
|
|
8
|
-
const [data, setData] = useState(initialData);
|
|
9
|
-
const [error, setError] = useState(initialError);
|
|
10
|
-
const [cancelSource, setCancelSource] = useState();
|
|
11
|
-
const cancelRequest = useCallback(() => {
|
|
12
|
-
if (cancelSource?.abort)
|
|
13
|
-
cancelSource.abort();
|
|
14
|
-
}, [cancelSource]);
|
|
15
|
-
const sendRequest = useCallback(async () => {
|
|
16
|
-
if (ignoreRequest === true)
|
|
17
|
-
return;
|
|
18
|
-
const url = new URL(stringUrl, window.location.origin);
|
|
19
|
-
if (query || params) {
|
|
20
|
-
url.search = new URLSearchParams(query || params || {}).toString();
|
|
21
|
-
}
|
|
22
|
-
const contentType = fetchProps.headers?.["Content-Type"] ||
|
|
23
|
-
fetchProps.headers?.["content-type"];
|
|
24
|
-
if (contentType === "application/x-www-form-urlencoded") {
|
|
25
|
-
fetchProps.body = new URLSearchParams(body || {});
|
|
26
|
-
}
|
|
27
|
-
else if (body) {
|
|
28
|
-
fetchProps.body = JSON.stringify(body);
|
|
29
|
-
}
|
|
30
|
-
const controller = new AbortController();
|
|
31
|
-
const requestTimeout = setTimeout(() => controller.abort(), timeout);
|
|
32
|
-
fetchProps.signal = controller.signal;
|
|
33
|
-
if (pending)
|
|
34
|
-
setPending2(true);
|
|
35
|
-
if (!pending)
|
|
36
|
-
setPending(true);
|
|
37
|
-
setError(undefined);
|
|
38
|
-
cancelRequest();
|
|
39
|
-
setCancelSource(controller);
|
|
40
|
-
if (onStart)
|
|
41
|
-
onStart();
|
|
42
|
-
try {
|
|
43
|
-
const response = await fetch(url, fetchProps);
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
throw new Error(JSON.stringify({
|
|
46
|
-
code: response.status,
|
|
47
|
-
text: response.statusText,
|
|
48
|
-
response: await response.text(),
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
const parsedResponse = await response[parser]();
|
|
53
|
-
setData(parsedResponse);
|
|
54
|
-
if (onSuccess)
|
|
55
|
-
onSuccess(parsedResponse);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
if (e.name !== "AbortError") {
|
|
60
|
-
let error;
|
|
61
|
-
try {
|
|
62
|
-
error = JSON.parse(e.toString().replace("Error:", "").trim());
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
error = { response: e.toString(), text: e.toString() };
|
|
66
|
-
}
|
|
67
|
-
setError(error);
|
|
68
|
-
if (onFail)
|
|
69
|
-
onFail(error);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
clearTimeout(requestTimeout);
|
|
74
|
-
if (pending) {
|
|
75
|
-
setPending2(undefined);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
setPending(undefined);
|
|
79
|
-
}
|
|
80
|
-
if (onFinish)
|
|
81
|
-
onFinish();
|
|
82
|
-
}
|
|
83
|
-
}, [
|
|
84
|
-
ignoreRequest,
|
|
85
|
-
stringUrl,
|
|
86
|
-
query,
|
|
87
|
-
params,
|
|
88
|
-
fetchProps,
|
|
89
|
-
body,
|
|
90
|
-
timeout,
|
|
91
|
-
parser,
|
|
92
|
-
onStart,
|
|
93
|
-
onSuccess,
|
|
94
|
-
onFail,
|
|
95
|
-
onFinish,
|
|
96
|
-
]);
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
sendRequest();
|
|
99
|
-
}, deps);
|
|
100
|
-
useInterval(() => {
|
|
101
|
-
sendRequest();
|
|
102
|
-
}, poll);
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
return () => {
|
|
105
|
-
if (ignoreCleanup !== true)
|
|
106
|
-
cancelRequest();
|
|
107
|
-
};
|
|
108
|
-
}, []);
|
|
109
|
-
return {
|
|
110
|
-
pending: pending || pending2,
|
|
111
|
-
data,
|
|
112
|
-
error,
|
|
113
|
-
sendRequest,
|
|
114
|
-
cancelRequest,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
export default useAsyncFetch;
|
package/dist/useInterval.d.ts
DELETED
package/dist/useInterval.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useRef, useEffect } from "react";
|
|
2
|
-
function useInterval(callback, poll) {
|
|
3
|
-
const callbackRef = useRef(() => { }); // noop
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
if (typeof callback === "function") {
|
|
6
|
-
callbackRef.current = callback;
|
|
7
|
-
}
|
|
8
|
-
}, [callback]);
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
if (typeof poll !== "number" || poll < 100)
|
|
11
|
-
return;
|
|
12
|
-
const interval = setInterval(() => callbackRef.current(), poll);
|
|
13
|
-
return () => clearInterval(interval);
|
|
14
|
-
}, [poll]);
|
|
15
|
-
}
|
|
16
|
-
export default useInterval;
|
|
17
|
-
// https://github.com/Hermanya/use-interval/blob/master/src/index.tsx
|