@faasjs/react 8.0.0-beta.20 → 8.0.0-beta.22
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/dist/index.d.ts +158 -216
- package/dist/index.mjs +295 -297
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -4,13 +4,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
/**
|
|
5
5
|
* Generate a random identifier with an optional prefix.
|
|
6
6
|
*
|
|
7
|
-
* @param prefix - Prefix prepended to the generated identifier.
|
|
8
|
-
* @param length - Length of the generated identifier excluding `prefix`. Must be between `8` and `18`.
|
|
9
|
-
* @returns Generated identifier string.
|
|
7
|
+
* @param {string} [prefix] - Prefix prepended to the generated identifier.
|
|
8
|
+
* @param {number} [length] - Length of the generated identifier excluding `prefix`. Must be between `8` and `18`.
|
|
9
|
+
* @returns {string} Generated identifier string.
|
|
10
10
|
* @throws {Error} When `length` is outside the supported `8` to `18` range.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```ts
|
|
14
|
+
* import { generateId } from '@faasjs/react'
|
|
15
|
+
*
|
|
14
16
|
* const id = generateId('prefix-')
|
|
15
17
|
*
|
|
16
18
|
* id.startsWith('prefix-') // true
|
|
@@ -133,9 +135,9 @@ function generateId(prefix = "", length = 18) {
|
|
|
133
135
|
* })
|
|
134
136
|
* ```
|
|
135
137
|
*
|
|
136
|
-
* @see ResponseProps for response property type
|
|
137
|
-
* @see ResponseError for error response handling
|
|
138
|
-
* @see FaasBrowserClient.action for method returning Response
|
|
138
|
+
* @see {@link ResponseProps} for response property type.
|
|
139
|
+
* @see {@link ResponseError} for error response handling.
|
|
140
|
+
* @see {@link FaasBrowserClient.action} for method returning Response.
|
|
139
141
|
*/
|
|
140
142
|
var Response = class {
|
|
141
143
|
/**
|
|
@@ -157,12 +159,12 @@ var Response = class {
|
|
|
157
159
|
/**
|
|
158
160
|
* Create a wrapped response object.
|
|
159
161
|
*
|
|
160
|
-
* @param props - Response properties including status, headers, body, and data.
|
|
161
|
-
* @param props.status - HTTP status code. Defaults to `200` when `data` or `body` exists, otherwise `204`.
|
|
162
|
-
* @param props.headers - Response headers keyed by header name.
|
|
163
|
-
* @param props.body - Raw response body to expose without additional parsing.
|
|
164
|
-
* @param props.data - Parsed response payload to expose on `response.data`.
|
|
165
|
-
* @returns Wrapped response instance.
|
|
162
|
+
* @param {ResponseProps<T>} [props] - Response properties including status, headers, body, and data.
|
|
163
|
+
* @param {number} [props.status] - HTTP status code. Defaults to `200` when `data` or `body` exists, otherwise `204`.
|
|
164
|
+
* @param {ResponseHeaders} [props.headers] - Response headers keyed by header name.
|
|
165
|
+
* @param {any} [props.body] - Raw response body to expose without additional parsing.
|
|
166
|
+
* @param {T} [props.data] - Parsed response payload to expose on `response.data`.
|
|
167
|
+
* @returns {Response<T>} Wrapped response instance.
|
|
166
168
|
*/
|
|
167
169
|
constructor(props = {}) {
|
|
168
170
|
this.status = props.status || (props.data || props.body ? 200 : 204);
|
|
@@ -264,9 +266,9 @@ var Response = class {
|
|
|
264
266
|
* - Use instanceof ResponseError to distinguish FaasJS errors from other JavaScript errors
|
|
265
267
|
* - The body property can contain structured error information from the server response
|
|
266
268
|
*
|
|
267
|
-
* @see FaasBrowserClient.action for how ResponseError is thrown in requests
|
|
268
|
-
* @see ResponseProps for the structure of response data
|
|
269
|
-
* @see setMock for mocking errors in tests
|
|
269
|
+
* @see {@link FaasBrowserClient.action} for how ResponseError is thrown in requests.
|
|
270
|
+
* @see {@link ResponseProps} for the structure of response data.
|
|
271
|
+
* @see {@link setMock} for mocking errors in tests.
|
|
270
272
|
*/
|
|
271
273
|
var ResponseError = class extends Error {
|
|
272
274
|
/**
|
|
@@ -308,7 +310,7 @@ let mock = null;
|
|
|
308
310
|
/**
|
|
309
311
|
* Set the global mock handler used by all {@link FaasBrowserClient} instances.
|
|
310
312
|
*
|
|
311
|
-
* @param handler - Mock handler, can be:
|
|
313
|
+
* @param {MockHandler | ResponseProps | Response | null | undefined} handler - Mock handler, can be:
|
|
312
314
|
* - MockHandler function: receives (action, params, options) and returns response data
|
|
313
315
|
* - ResponseProps object: static response data
|
|
314
316
|
* - Response instance: pre-configured Response object
|
|
@@ -466,8 +468,8 @@ function setMock(handler) {
|
|
|
466
468
|
*
|
|
467
469
|
* @throws {Error} When baseUrl does not end with '/'
|
|
468
470
|
*
|
|
469
|
-
* @see setMock for testing support
|
|
470
|
-
* @see ResponseError for error handling
|
|
471
|
+
* @see {@link setMock} for testing support.
|
|
472
|
+
* @see {@link ResponseError} for error handling.
|
|
471
473
|
*/
|
|
472
474
|
var FaasBrowserClient = class {
|
|
473
475
|
/**
|
|
@@ -485,8 +487,8 @@ var FaasBrowserClient = class {
|
|
|
485
487
|
/**
|
|
486
488
|
* Creates a new FaasBrowserClient instance.
|
|
487
489
|
*
|
|
488
|
-
* @param baseUrl - Base URL for all API requests. Must end with `/`. Defaults to `/` for relative requests.
|
|
489
|
-
* @param options - Default request options such as headers, hooks, request override, or stream mode.
|
|
490
|
+
* @param {BaseUrl} [baseUrl] - Base URL for all API requests. Must end with `/`. Defaults to `/` for relative requests.
|
|
491
|
+
* @param {Options} [options] - Default request options such as headers, hooks, request override, or stream mode.
|
|
490
492
|
* See {@link Options} for supported request fields such as `headers`, `beforeRequest`,
|
|
491
493
|
* `request`, `baseUrl`, and `stream`.
|
|
492
494
|
*
|
|
@@ -552,16 +554,16 @@ var FaasBrowserClient = class {
|
|
|
552
554
|
* Makes a request to a FaasJS function.
|
|
553
555
|
*
|
|
554
556
|
* @template PathOrData - The function path or data type for type safety
|
|
555
|
-
* @param action - The function path to call. Converted to lowercase when constructing the URL.
|
|
557
|
+
* @param {FaasAction<PathOrData>} action - The function path to call. Converted to lowercase when constructing the URL.
|
|
556
558
|
* Must be a non-empty string.
|
|
557
|
-
* @param params - The parameters to send to the function. Will be serialized as JSON.
|
|
559
|
+
* @param {FaasParams<PathOrData>} [params] - The parameters to send to the function. Will be serialized as JSON.
|
|
558
560
|
* Optional if the function accepts no parameters.
|
|
559
|
-
* @param options - Optional request options that override client defaults.
|
|
561
|
+
* @param {Options} [options] - Optional request options that override client defaults.
|
|
560
562
|
* Supports headers, beforeRequest hook, custom request function, baseUrl override, and streaming mode.
|
|
561
563
|
* See {@link Options} for supported request fields such as `headers`, `beforeRequest`,
|
|
562
564
|
* `request`, `baseUrl`, and `stream`.
|
|
563
565
|
*
|
|
564
|
-
* @returns A promise resolving to the wrapped FaasJS response. When `options.stream`
|
|
566
|
+
* @returns {Promise<Response<FaasData<PathOrData>>>} A promise resolving to the wrapped FaasJS response. When `options.stream`
|
|
565
567
|
* is `true`, the runtime returns the native fetch response so callers can read the stream.
|
|
566
568
|
*
|
|
567
569
|
* @throws {Error} When action is not provided or is empty
|
|
@@ -745,12 +747,12 @@ var FaasBrowserClient = class {
|
|
|
745
747
|
*
|
|
746
748
|
* @template PathOrData - Action path or response data type used for inference.
|
|
747
749
|
*
|
|
748
|
-
* @param action - Action path to invoke.
|
|
749
|
-
* @param params - Parameters sent to the action.
|
|
750
|
-
* @param options - Optional per-request overrides such as headers or base URL.
|
|
751
|
-
* See the
|
|
750
|
+
* @param {FaasAction<PathOrData>} action - Action path to invoke.
|
|
751
|
+
* @param {FaasParams<PathOrData>} params - Parameters sent to the action.
|
|
752
|
+
* @param {Options} [options] - Optional per-request overrides such as headers or base URL.
|
|
753
|
+
* See the browser-client `Options` type for supported fields such as `headers`, `beforeRequest`,
|
|
752
754
|
* `request`, `baseUrl`, and `stream`.
|
|
753
|
-
* @returns Response returned by the active browser client.
|
|
755
|
+
* @returns {Promise<Response<FaasData<PathOrData>>>} Response returned by the active browser client.
|
|
754
756
|
* @throws {ResponseError} When the request fails and the active client does not recover inside `onError`.
|
|
755
757
|
*
|
|
756
758
|
* @example
|
|
@@ -781,9 +783,9 @@ const AsyncFunction = (async () => {}).constructor;
|
|
|
781
783
|
* It handles various data types including primitives, arrays, dates, regular expressions, functions,
|
|
782
784
|
* maps, sets, and promises.
|
|
783
785
|
*
|
|
784
|
-
* @param a - The first value to compare.
|
|
785
|
-
* @param b - The second value to compare.
|
|
786
|
-
* @returns `true` if the values are deeply equal, `false` otherwise.
|
|
786
|
+
* @param {any} a - The first value to compare.
|
|
787
|
+
* @param {any} b - The second value to compare.
|
|
788
|
+
* @returns {boolean} `true` if the values are deeply equal, `false` otherwise.
|
|
787
789
|
*
|
|
788
790
|
* @example
|
|
789
791
|
* ```ts
|
|
@@ -824,8 +826,8 @@ function equal(a, b) {
|
|
|
824
826
|
/**
|
|
825
827
|
* Custom hook that memoizes a value using deep equality comparison.
|
|
826
828
|
*
|
|
827
|
-
* @param value - The value to be memoized.
|
|
828
|
-
* @returns The memoized value.
|
|
829
|
+
* @param {any} value - The value to be memoized.
|
|
830
|
+
* @returns {any} The memoized value.
|
|
829
831
|
*
|
|
830
832
|
* @example
|
|
831
833
|
* ```tsx
|
|
@@ -855,9 +857,9 @@ function useEqualSignal(value) {
|
|
|
855
857
|
/**
|
|
856
858
|
* Custom hook that works like `useEffect` but uses deep comparison on dependencies.
|
|
857
859
|
*
|
|
858
|
-
* @param callback - The effect callback function to run.
|
|
859
|
-
* @param dependencies - The list of dependencies for the effect.
|
|
860
|
-
* @returns The result of the `useEffect` hook with memoized dependencies.
|
|
860
|
+
* @param {React.EffectCallback} callback - The effect callback function to run.
|
|
861
|
+
* @param {any[]} dependencies - The list of dependencies for the effect.
|
|
862
|
+
* @returns {void} The result of the `useEffect` hook with memoized dependencies.
|
|
861
863
|
*
|
|
862
864
|
* @example
|
|
863
865
|
* ```tsx
|
|
@@ -880,9 +882,9 @@ function useEqualEffect(callback, dependencies) {
|
|
|
880
882
|
*
|
|
881
883
|
* @template T - Memoized value type returned by the callback.
|
|
882
884
|
*
|
|
883
|
-
* @param callback - The callback function to run.
|
|
884
|
-
* @param dependencies - The list of dependencies.
|
|
885
|
-
* @returns The result of the `useMemo` hook with memoized dependencies.
|
|
885
|
+
* @param {() => T} callback - The callback function to run.
|
|
886
|
+
* @param {any[]} dependencies - The list of dependencies.
|
|
887
|
+
* @returns {T} The result of the `useMemo` hook with memoized dependencies.
|
|
886
888
|
*
|
|
887
889
|
* @example
|
|
888
890
|
* ```tsx
|
|
@@ -908,9 +910,9 @@ function useEqualMemo(callback, dependencies) {
|
|
|
908
910
|
*
|
|
909
911
|
* @template T - Callback signature to memoize.
|
|
910
912
|
*
|
|
911
|
-
* @param callback - The callback function to run.
|
|
912
|
-
* @param dependencies - The list of dependencies.
|
|
913
|
-
* @returns The result of the `useCallback` hook with memoized dependencies.
|
|
913
|
+
* @param {T} callback - The callback function to run.
|
|
914
|
+
* @param {any[]} dependencies - The list of dependencies.
|
|
915
|
+
* @returns {T} The result of the `useCallback` hook with memoized dependencies.
|
|
914
916
|
*
|
|
915
917
|
* @example
|
|
916
918
|
* ```tsx
|
|
@@ -934,16 +936,16 @@ function useEqualCallback(callback, dependencies) {
|
|
|
934
936
|
* The wrapper defers rendering `children` or `render` until the first request
|
|
935
937
|
* completes, then keeps passing the latest request state to the rendered output.
|
|
936
938
|
*
|
|
937
|
-
* @param props - Wrapper props controlling the request and rendered fallback.
|
|
938
|
-
* @param props.render - Render prop that receives the resolved Faas request state.
|
|
939
|
-
* @param props.children - Child element cloned with injected Faas request state.
|
|
940
|
-
* @param props.fallback - Element rendered before the first successful load.
|
|
941
|
-
* @param props.action - Action path to request.
|
|
942
|
-
* @param props.params - Params sent to the action.
|
|
943
|
-
* @param props.onDataChange - Callback invoked when the resolved data value changes.
|
|
944
|
-
* @param props.data - Controlled data value used instead of internal state.
|
|
945
|
-
* @param props.setData - Controlled setter used instead of internal state.
|
|
946
|
-
* @param props.baseUrl - Base URL override used for this wrapper instance.
|
|
939
|
+
* @param {FaasDataWrapperProps<PathOrData>} props - Wrapper props controlling the request and rendered fallback.
|
|
940
|
+
* @param {(args: FaasDataInjection<PathOrData>) => JSX.Element | JSX.Element[]} [props.render] - Render prop that receives the resolved Faas request state.
|
|
941
|
+
* @param {React.ReactElement<Partial<FaasDataInjection<PathOrData>>>} [props.children] - Child element cloned with injected Faas request state.
|
|
942
|
+
* @param {JSX.Element | false} [props.fallback] - Element rendered before the first successful load.
|
|
943
|
+
* @param {FaasAction<PathOrData>} props.action - Action path to request.
|
|
944
|
+
* @param {FaasParams<PathOrData>} [props.params] - Params sent to the action.
|
|
945
|
+
* @param {(args: FaasDataInjection<PathOrData>) => void} [props.onDataChange] - Callback invoked when the resolved data value changes.
|
|
946
|
+
* @param {FaasData<PathOrData>} [props.data] - Controlled data value used instead of internal state.
|
|
947
|
+
* @param {React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>} [props.setData] - Controlled setter used instead of internal state.
|
|
948
|
+
* @param {BaseUrl} [props.baseUrl] - Base URL override used for this wrapper instance.
|
|
947
949
|
*
|
|
948
950
|
* @example
|
|
949
951
|
* ```tsx
|
|
@@ -1052,9 +1054,9 @@ Object.assign(FaasDataWrapper, { displayName: "FaasDataWrapper" });
|
|
|
1052
1054
|
*
|
|
1053
1055
|
* @template PathOrData - Action path or response data type used for inference.
|
|
1054
1056
|
* @template TComponentProps - Component props including injected Faas data fields.
|
|
1055
|
-
* @param Component - Component that consumes injected Faas data props.
|
|
1056
|
-
* @param faasProps - Request configuration forwarded to `FaasDataWrapper`.
|
|
1057
|
-
* @returns Component that accepts the original props minus the injected Faas data fields.
|
|
1057
|
+
* @param {React.FC<TComponentProps>} Component - Component that consumes injected Faas data props.
|
|
1058
|
+
* @param {FaasDataWrapperProps<PathOrData>} faasProps - Request configuration forwarded to `FaasDataWrapper`.
|
|
1059
|
+
* @returns {React.FC<Omit<TComponentProps, keyof FaasDataInjection<PathOrData>> & Record<string, any>>} Component that accepts the original props minus the injected Faas data fields.
|
|
1058
1060
|
*
|
|
1059
1061
|
* @example
|
|
1060
1062
|
* ```tsx
|
|
@@ -1083,71 +1085,59 @@ function withFaasData(Component, faasProps) {
|
|
|
1083
1085
|
});
|
|
1084
1086
|
}
|
|
1085
1087
|
//#endregion
|
|
1086
|
-
//#region src/
|
|
1088
|
+
//#region src/useFaasRequest.ts
|
|
1087
1089
|
/**
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
* @template
|
|
1095
|
-
*
|
|
1096
|
-
* @param
|
|
1097
|
-
* @param
|
|
1098
|
-
* @param
|
|
1099
|
-
* @param
|
|
1100
|
-
* @param
|
|
1101
|
-
* @param
|
|
1102
|
-
* @param
|
|
1103
|
-
* @
|
|
1104
|
-
* @param options.baseUrl - Base URL override used for this hook instance.
|
|
1105
|
-
* @returns Request state and helper methods described by {@link FaasDataInjection}.
|
|
1106
|
-
*
|
|
1090
|
+
* Run the shared request lifecycle used by the higher-level FaasJS React hooks.
|
|
1091
|
+
*
|
|
1092
|
+
* It manages loading state, abort signals, debounce timing, retry-on-fetch-failure,
|
|
1093
|
+
* and queued reload promises while delegating the actual transport to `send`.
|
|
1094
|
+
*
|
|
1095
|
+
* @template Params - Request params type tracked by the lifecycle.
|
|
1096
|
+
* @template Result - Successful response payload type.
|
|
1097
|
+
* @template RequestPromise - Promise type exposed through `promiseRef`.
|
|
1098
|
+
* @param {UseFaasRequestArgs<Params, Result, RequestPromise>} args - Request lifecycle configuration.
|
|
1099
|
+
* @param {string} args.action - Action path or request key used to trigger the lifecycle.
|
|
1100
|
+
* @param {Params} args.defaultParams - Initial params value stored by the lifecycle.
|
|
1101
|
+
* @param {Pick<SharedUseFaasOptions<Params, Result>, 'params' | 'skip' | 'debounce' | 'baseUrl'>} args.options - Shared request options used by the lifecycle.
|
|
1102
|
+
* @param {() => void} [args.beforeSend] - Optional callback invoked immediately before a request starts.
|
|
1103
|
+
* @param {(result: Result) => void} [args.onSuccess] - Optional callback invoked after a successful response.
|
|
1104
|
+
* @param {UseFaasRequestArgs<Params, Result, RequestPromise>['send']} args.send - Transport function responsible for creating and resolving the request.
|
|
1105
|
+
* @returns Shared request state, reload helpers, and refs used by `useFaas` and `useFaasStream`.
|
|
1107
1106
|
* @example
|
|
1108
|
-
* ```
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1107
|
+
* ```ts
|
|
1108
|
+
* function useUserRequest(id: number) {
|
|
1109
|
+
* return useFaasRequest({
|
|
1110
|
+
* action: '/pages/users/get',
|
|
1111
|
+
* defaultParams: { id },
|
|
1112
|
+
* options: {},
|
|
1113
|
+
* send: async ({ action, params, signal, client, setPromise }) => {
|
|
1114
|
+
* const promise = client.faas(action, params, { signal })
|
|
1115
1115
|
*
|
|
1116
|
-
*
|
|
1117
|
-
* return (
|
|
1118
|
-
* <div>
|
|
1119
|
-
* <div>Load failed: {error.message}</div>
|
|
1120
|
-
* <button type="button" onClick={() => reload()}>
|
|
1121
|
-
* Retry
|
|
1122
|
-
* </button>
|
|
1123
|
-
* </div>
|
|
1124
|
-
* )
|
|
1125
|
-
* }
|
|
1116
|
+
* setPromise(promise)
|
|
1126
1117
|
*
|
|
1127
|
-
*
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
* <button type="button" onClick={() => reload()}>
|
|
1131
|
-
* Refresh
|
|
1132
|
-
* </button>
|
|
1133
|
-
* </div>
|
|
1134
|
-
* )
|
|
1118
|
+
* return (await promise).data as { name: string }
|
|
1119
|
+
* },
|
|
1120
|
+
* })
|
|
1135
1121
|
* }
|
|
1136
1122
|
* ```
|
|
1137
1123
|
*/
|
|
1138
|
-
function
|
|
1124
|
+
function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess, send }) {
|
|
1139
1125
|
const [loading, setLoading] = useState(true);
|
|
1140
|
-
const [data, setData] = useState();
|
|
1141
1126
|
const [error, setError] = useState();
|
|
1142
1127
|
const [params, setParams] = useState(defaultParams);
|
|
1143
1128
|
const [reloadTimes, setReloadTimes] = useState(0);
|
|
1144
|
-
const [fails, setFails] = useState(0);
|
|
1145
1129
|
const [skip, setSkip] = useState(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
|
|
1146
1130
|
const promiseRef = useRef(null);
|
|
1147
1131
|
const controllerRef = useRef(null);
|
|
1148
|
-
const
|
|
1132
|
+
const failedOnceRef = useRef(false);
|
|
1149
1133
|
const pendingReloadsRef = useRef(/* @__PURE__ */ new Map());
|
|
1150
1134
|
const reloadCounterRef = useRef(0);
|
|
1135
|
+
const beforeSendRef = useRef(beforeSend);
|
|
1136
|
+
const onSuccessRef = useRef(onSuccess);
|
|
1137
|
+
const sendRef = useRef(send);
|
|
1138
|
+
beforeSendRef.current = beforeSend;
|
|
1139
|
+
onSuccessRef.current = onSuccess;
|
|
1140
|
+
sendRef.current = send;
|
|
1151
1141
|
useEqualEffect(() => {
|
|
1152
1142
|
setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
|
|
1153
1143
|
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
@@ -1160,50 +1150,63 @@ function useFaas(action, defaultParams, options = {}) {
|
|
|
1160
1150
|
return;
|
|
1161
1151
|
}
|
|
1162
1152
|
setLoading(true);
|
|
1153
|
+
beforeSendRef.current?.();
|
|
1154
|
+
failedOnceRef.current = false;
|
|
1163
1155
|
const controller = new AbortController();
|
|
1164
1156
|
controllerRef.current = controller;
|
|
1165
1157
|
const client = getClient(options.baseUrl);
|
|
1166
|
-
const requestParams = options.params
|
|
1167
|
-
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1158
|
+
const requestParams = options.params || params;
|
|
1159
|
+
const rejectPending = (reason) => {
|
|
1160
|
+
for (const { reject } of pendingReloadsRef.current.values()) reject(reason);
|
|
1161
|
+
pendingReloadsRef.current.clear();
|
|
1162
|
+
};
|
|
1163
|
+
const resolvePending = (value) => {
|
|
1164
|
+
for (const { resolve } of pendingReloadsRef.current.values()) resolve(value);
|
|
1165
|
+
pendingReloadsRef.current.clear();
|
|
1166
|
+
};
|
|
1167
|
+
const run = () => {
|
|
1168
|
+
sendRef.current({
|
|
1169
|
+
action,
|
|
1170
|
+
params: requestParams,
|
|
1171
|
+
signal: controller.signal,
|
|
1172
|
+
client,
|
|
1173
|
+
setPromise: (promise) => {
|
|
1174
|
+
promiseRef.current = promise;
|
|
1175
|
+
}
|
|
1176
|
+
}).then((result) => {
|
|
1177
|
+
failedOnceRef.current = false;
|
|
1173
1178
|
setError(null);
|
|
1174
|
-
|
|
1175
|
-
else localSetData(nextData);
|
|
1179
|
+
onSuccessRef.current?.(result);
|
|
1176
1180
|
setLoading(false);
|
|
1177
|
-
|
|
1178
|
-
pendingReloadsRef.current.clear();
|
|
1181
|
+
resolvePending(result);
|
|
1179
1182
|
}).catch(async (e) => {
|
|
1180
|
-
if (typeof e?.message === "string" && e.message.toLowerCase().
|
|
1181
|
-
if (!
|
|
1183
|
+
if (typeof e?.message === "string" && e.message.toLowerCase().includes("aborted")) return;
|
|
1184
|
+
if (!failedOnceRef.current && typeof e?.message === "string" && e.message.includes("Failed to fetch")) {
|
|
1185
|
+
failedOnceRef.current = true;
|
|
1182
1186
|
console.warn(`FaasReactClient: ${e.message} retry...`);
|
|
1183
|
-
|
|
1184
|
-
return
|
|
1187
|
+
run();
|
|
1188
|
+
return;
|
|
1185
1189
|
}
|
|
1186
|
-
let
|
|
1190
|
+
let nextError = e;
|
|
1187
1191
|
if (client.onError) try {
|
|
1188
|
-
await client.onError(action, requestParams)(e);
|
|
1192
|
+
await client.onError(action, requestParams || Object.create(null))(e);
|
|
1189
1193
|
} catch (newError) {
|
|
1190
|
-
|
|
1194
|
+
nextError = newError;
|
|
1191
1195
|
}
|
|
1192
|
-
setError(
|
|
1196
|
+
setError(nextError);
|
|
1193
1197
|
setLoading(false);
|
|
1194
|
-
|
|
1195
|
-
pendingReloadsRef.current.clear();
|
|
1198
|
+
rejectPending(nextError);
|
|
1196
1199
|
});
|
|
1197
|
-
}
|
|
1200
|
+
};
|
|
1198
1201
|
if (options.debounce) {
|
|
1199
|
-
const timeout = setTimeout(
|
|
1202
|
+
const timeout = setTimeout(run, options.debounce);
|
|
1200
1203
|
return () => {
|
|
1201
1204
|
clearTimeout(timeout);
|
|
1202
1205
|
controllerRef.current?.abort();
|
|
1203
1206
|
setLoading(false);
|
|
1204
1207
|
};
|
|
1205
1208
|
}
|
|
1206
|
-
|
|
1209
|
+
run();
|
|
1207
1210
|
return () => {
|
|
1208
1211
|
controllerRef.current?.abort();
|
|
1209
1212
|
setLoading(false);
|
|
@@ -1214,35 +1217,111 @@ function useFaas(action, defaultParams, options = {}) {
|
|
|
1214
1217
|
reloadTimes,
|
|
1215
1218
|
skip
|
|
1216
1219
|
]);
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1220
|
+
return {
|
|
1221
|
+
loading,
|
|
1222
|
+
error,
|
|
1223
|
+
params,
|
|
1224
|
+
reloadTimes,
|
|
1225
|
+
reload: useEqualCallback((nextParams) => {
|
|
1226
|
+
if (skip) setSkip(false);
|
|
1227
|
+
if (nextParams) setParams(nextParams);
|
|
1228
|
+
const reloadCounter = ++reloadCounterRef.current;
|
|
1229
|
+
return new Promise((resolve, reject) => {
|
|
1230
|
+
pendingReloadsRef.current.set(reloadCounter, {
|
|
1231
|
+
resolve,
|
|
1232
|
+
reject
|
|
1233
|
+
});
|
|
1234
|
+
setReloadTimes((prev) => prev + 1);
|
|
1225
1235
|
});
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1236
|
+
}, [skip]),
|
|
1237
|
+
promiseRef,
|
|
1238
|
+
setError,
|
|
1239
|
+
setLoading
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
//#endregion
|
|
1243
|
+
//#region src/useFaas.tsx
|
|
1244
|
+
/**
|
|
1245
|
+
* Request FaasJS data and keep request state in React state.
|
|
1246
|
+
*
|
|
1247
|
+
* `useFaas` is the default hook for standard FaasJS request-response flows in React.
|
|
1248
|
+
* It sends an initial request unless `skip` is enabled, and returns request state
|
|
1249
|
+
* plus helpers for reloading, updating data, and handling errors.
|
|
1250
|
+
*
|
|
1251
|
+
* @template PathOrData - Action path or response data type used for inference.
|
|
1252
|
+
*
|
|
1253
|
+
* @param {FaasAction<PathOrData>} action - Action path to invoke.
|
|
1254
|
+
* @param {FaasParams<PathOrData>} defaultParams - Params used for the initial request and future reloads.
|
|
1255
|
+
* @param {useFaasOptions<PathOrData>} [options] - Optional hook configuration such as controlled data, skip logic, debounce timing, and base URL overrides.
|
|
1256
|
+
* See the `useFaasOptions` type for `params`, `data`, `setData`, `skip`, `debounce`, and `baseUrl`.
|
|
1257
|
+
* @returns {FaasDataInjection<PathOrData>} Request state and helper methods described by {@link FaasDataInjection}.
|
|
1258
|
+
*
|
|
1259
|
+
* @example
|
|
1260
|
+
* ```tsx
|
|
1261
|
+
* import { useFaas } from '@faasjs/react'
|
|
1262
|
+
*
|
|
1263
|
+
* function Profile({ id }: { id: number }) {
|
|
1264
|
+
* const { data, error, loading, reload } = useFaas('/pages/users/get', { id })
|
|
1265
|
+
*
|
|
1266
|
+
* if (loading) return <div>Loading...</div>
|
|
1267
|
+
*
|
|
1268
|
+
* if (error) {
|
|
1269
|
+
* return (
|
|
1270
|
+
* <div>
|
|
1271
|
+
* <div>Load failed: {error.message}</div>
|
|
1272
|
+
* <button type="button" onClick={() => reload()}>
|
|
1273
|
+
* Retry
|
|
1274
|
+
* </button>
|
|
1275
|
+
* </div>
|
|
1276
|
+
* )
|
|
1277
|
+
* }
|
|
1278
|
+
*
|
|
1279
|
+
* return (
|
|
1280
|
+
* <div>
|
|
1281
|
+
* <span>{data.name}</span>
|
|
1282
|
+
* <button type="button" onClick={() => reload()}>
|
|
1283
|
+
* Refresh
|
|
1284
|
+
* </button>
|
|
1285
|
+
* </div>
|
|
1286
|
+
* )
|
|
1287
|
+
* }
|
|
1288
|
+
* ```
|
|
1289
|
+
*/
|
|
1290
|
+
function useFaas(action, defaultParams, options = {}) {
|
|
1291
|
+
const [data, setData] = useState();
|
|
1292
|
+
const localSetData = setData;
|
|
1293
|
+
const request = useFaasRequest({
|
|
1294
|
+
action,
|
|
1295
|
+
defaultParams,
|
|
1296
|
+
options,
|
|
1297
|
+
onSuccess: (nextData) => {
|
|
1298
|
+
if (options.setData) options.setData(nextData);
|
|
1299
|
+
else localSetData(nextData);
|
|
1300
|
+
},
|
|
1301
|
+
send: ({ action, params, signal, client, setPromise }) => {
|
|
1302
|
+
const promise = client.faas(action, params, { signal });
|
|
1303
|
+
setPromise(promise);
|
|
1304
|
+
return promise.then((response) => response.data);
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1229
1307
|
const currentData = options.data ?? data;
|
|
1230
|
-
const currentPromise = promiseRef.current ?? Promise.resolve({});
|
|
1308
|
+
const currentPromise = request.promiseRef.current ?? Promise.resolve({});
|
|
1309
|
+
const updateData = options.setData ?? localSetData;
|
|
1231
1310
|
return {
|
|
1232
1311
|
action,
|
|
1233
|
-
params,
|
|
1234
|
-
loading,
|
|
1312
|
+
params: request.params,
|
|
1313
|
+
loading: request.loading,
|
|
1235
1314
|
data: currentData,
|
|
1236
|
-
reloadTimes,
|
|
1237
|
-
error,
|
|
1315
|
+
reloadTimes: request.reloadTimes,
|
|
1316
|
+
error: request.error,
|
|
1238
1317
|
promise: currentPromise,
|
|
1239
|
-
reload,
|
|
1240
|
-
setData:
|
|
1241
|
-
setLoading,
|
|
1318
|
+
reload: request.reload,
|
|
1319
|
+
setData: updateData,
|
|
1320
|
+
setLoading: request.setLoading,
|
|
1242
1321
|
setPromise: (newPromise) => {
|
|
1243
|
-
promiseRef.current = typeof newPromise === "function" ? newPromise(currentPromise) : newPromise;
|
|
1322
|
+
request.promiseRef.current = typeof newPromise === "function" ? newPromise(currentPromise) : newPromise;
|
|
1244
1323
|
},
|
|
1245
|
-
setError
|
|
1324
|
+
setError: request.setError
|
|
1246
1325
|
};
|
|
1247
1326
|
}
|
|
1248
1327
|
//#endregion
|
|
@@ -1254,13 +1333,13 @@ const clients = {};
|
|
|
1254
1333
|
* The returned client is stored by `baseUrl` and becomes the default client
|
|
1255
1334
|
* used by helpers such as {@link faas} and {@link useFaas}.
|
|
1256
1335
|
*
|
|
1257
|
-
* @param options - Client configuration including base URL, default request options, and error hooks.
|
|
1258
|
-
* @param options.baseUrl - Base URL used to register and route the client instance.
|
|
1259
|
-
* @param options.options - Default browser-client request options forwarded to `FaasBrowserClient`.
|
|
1260
|
-
* @param options.onError - Hook factory used to handle failed `faas` and `useFaas` requests.
|
|
1336
|
+
* @param {FaasReactClientOptions} [options] - Client configuration including base URL, default request options, and error hooks.
|
|
1337
|
+
* @param {BaseUrl} [options.baseUrl] - Base URL used to register and route the client instance.
|
|
1338
|
+
* @param {Options} [options.options] - Default browser-client request options forwarded to `FaasBrowserClient`.
|
|
1339
|
+
* @param {OnError} [options.onError] - Hook factory used to handle failed `faas` and `useFaas` requests.
|
|
1261
1340
|
* See {@link Options} for supported browser-client request fields such as `headers`,
|
|
1262
1341
|
* `beforeRequest`, `request`, `baseUrl`, and `stream`.
|
|
1263
|
-
* @returns Registered FaasReactClient instance.
|
|
1342
|
+
* @returns {FaasReactClientInstance} Registered FaasReactClient instance.
|
|
1264
1343
|
*
|
|
1265
1344
|
* @example
|
|
1266
1345
|
* ```ts
|
|
@@ -1313,8 +1392,8 @@ function FaasReactClient(options = { baseUrl: "/" }) {
|
|
|
1313
1392
|
* different base URLs. In normal single-client app code, prefer the default
|
|
1314
1393
|
* `faas`, `useFaas`, or `FaasReactClient` setup directly.
|
|
1315
1394
|
*
|
|
1316
|
-
* @param host - Registered base URL to look up. Omit it to use the default client.
|
|
1317
|
-
* @returns Registered or newly created FaasReactClient instance.
|
|
1395
|
+
* @param {string} [host] - Registered base URL to look up. Omit it to use the default client.
|
|
1396
|
+
* @returns {FaasReactClientInstance} Registered or newly created FaasReactClient instance.
|
|
1318
1397
|
*
|
|
1319
1398
|
* @example
|
|
1320
1399
|
* ```ts
|
|
@@ -1347,7 +1426,8 @@ function getClient(host) {
|
|
|
1347
1426
|
* Returns a constant value that is created by the given function.
|
|
1348
1427
|
*
|
|
1349
1428
|
* @template T - Constant value type returned by the initializer.
|
|
1350
|
-
* @param fn - Initializer that runs only once for the current component instance.
|
|
1429
|
+
* @param {() => T} fn - Initializer that runs only once for the current component instance.
|
|
1430
|
+
* @returns {T} Stable value returned by the initializer.
|
|
1351
1431
|
*
|
|
1352
1432
|
* @example
|
|
1353
1433
|
* ```tsx
|
|
@@ -1395,10 +1475,10 @@ var ErrorBoundary = class extends Component {
|
|
|
1395
1475
|
/**
|
|
1396
1476
|
* Create an error boundary with empty error state.
|
|
1397
1477
|
*
|
|
1398
|
-
* @param props - Boundary props.
|
|
1399
|
-
* @param props.children - Descendant elements protected by the boundary.
|
|
1400
|
-
* @param props.onError - Callback invoked after a render error is captured.
|
|
1401
|
-
* @param props.errorChildren - Custom fallback element that receives error details.
|
|
1478
|
+
* @param {ErrorBoundaryProps} props - Boundary props.
|
|
1479
|
+
* @param {ReactNode} [props.children] - Descendant elements protected by the boundary.
|
|
1480
|
+
* @param {(error: Error | null, info: any) => void} [props.onError] - Callback invoked after a render error is captured.
|
|
1481
|
+
* @param {ReactElement<ErrorChildrenProps>} [props.errorChildren] - Custom fallback element that receives error details.
|
|
1402
1482
|
*/
|
|
1403
1483
|
constructor(props) {
|
|
1404
1484
|
super(props);
|
|
@@ -1410,8 +1490,8 @@ var ErrorBoundary = class extends Component {
|
|
|
1410
1490
|
/**
|
|
1411
1491
|
* Capture rendering errors from descendant components.
|
|
1412
1492
|
*
|
|
1413
|
-
* @param error - Caught render error.
|
|
1414
|
-
* @param info - React component stack metadata.
|
|
1493
|
+
* @param {Error} error - Caught render error.
|
|
1494
|
+
* @param {ErrorInfo} info - React component stack metadata.
|
|
1415
1495
|
*/
|
|
1416
1496
|
componentDidCatch(error, info) {
|
|
1417
1497
|
this.setState({
|
|
@@ -1444,12 +1524,12 @@ var ErrorBoundary = class extends Component {
|
|
|
1444
1524
|
/**
|
|
1445
1525
|
* Conditionally wrap children with another component.
|
|
1446
1526
|
*
|
|
1447
|
-
* @param props - Wrapper condition, wrapper component, and child content.
|
|
1448
|
-
* @param props.condition - When `true`, wrap children with `Wrapper`.
|
|
1449
|
-
* @param props.Wrapper - Component used as the wrapper when the condition passes.
|
|
1450
|
-
* @param props.wrapperProps - Props forwarded to the wrapper component.
|
|
1451
|
-
* @param props.children - Content rendered directly or inside the wrapper.
|
|
1452
|
-
* @returns Wrapped children or the original children when `condition` is false.
|
|
1527
|
+
* @param {OptionalWrapperProps} props - Wrapper condition, wrapper component, and child content.
|
|
1528
|
+
* @param {boolean} props.condition - When `true`, wrap children with `Wrapper`.
|
|
1529
|
+
* @param {OptionalWrapperProps['Wrapper']} props.Wrapper - Component used as the wrapper when the condition passes.
|
|
1530
|
+
* @param {OptionalWrapperProps['wrapperProps']} [props.wrapperProps] - Props forwarded to the wrapper component.
|
|
1531
|
+
* @param {ReactNode} props.children - Content rendered directly or inside the wrapper.
|
|
1532
|
+
* @returns {ReactNode} Wrapped children or the original children when `condition` is false.
|
|
1453
1533
|
*
|
|
1454
1534
|
* @example
|
|
1455
1535
|
* ```tsx
|
|
@@ -1481,8 +1561,8 @@ OptionalWrapper.displayName = "OptionalWrapper";
|
|
|
1481
1561
|
* Create local state entries and matching setters for each key in an object.
|
|
1482
1562
|
*
|
|
1483
1563
|
* @template T - A generic type that extends a record with string keys and any values.
|
|
1484
|
-
* @param initialStates - Object whose keys become state values and `setXxx` setters.
|
|
1485
|
-
* @returns Object containing the original keys plus generated setter functions.
|
|
1564
|
+
* @param {T} initialStates - Object whose keys become state values and `setXxx` setters.
|
|
1565
|
+
* @returns {StatesWithSetters<T>} Object containing the original keys plus generated setter functions.
|
|
1486
1566
|
*
|
|
1487
1567
|
* @example
|
|
1488
1568
|
* ```tsx
|
|
@@ -1514,8 +1594,8 @@ function useSplittingState(initialStates) {
|
|
|
1514
1594
|
* subscribe to the values they access.
|
|
1515
1595
|
*
|
|
1516
1596
|
* @template T - Context value shape exposed by the provider and hook.
|
|
1517
|
-
* @param defaultValue - Default value map or key list used to create split contexts.
|
|
1518
|
-
* @returns Provider and hook helpers for the split context.
|
|
1597
|
+
* @param {Record<string, any> | (keyof T)[]} defaultValue - Default value map or key list used to create split contexts.
|
|
1598
|
+
* @returns {{ Provider<NewT extends T = T>(this: void, props: { value?: Partial<NewT>; children: ReactNode; memo?: true | any[]; initializeStates?: Partial<NewT> }): ReactNode; use<NewT extends T = T>(this: void): Readonly<NewT> }} Provider and hook helpers for the split context.
|
|
1519
1599
|
*
|
|
1520
1600
|
* @example
|
|
1521
1601
|
* ```tsx
|
|
@@ -1601,16 +1681,11 @@ function createSplittingContext(defaultValue) {
|
|
|
1601
1681
|
* It sends a streaming request, appends decoded text chunks to `data`, and
|
|
1602
1682
|
* exposes reload helpers for retrying the same action.
|
|
1603
1683
|
*
|
|
1604
|
-
* @param action - Action path to invoke.
|
|
1605
|
-
* @param defaultParams - Params used for the initial request and future reloads.
|
|
1606
|
-
* @param options - Optional hook configuration such as controlled
|
|
1607
|
-
*
|
|
1608
|
-
* @
|
|
1609
|
-
* @param options.setData - Controlled setter used instead of the hook's internal `setData`.
|
|
1610
|
-
* @param options.skip - Boolean or predicate that suppresses the automatic request until `reload()` runs.
|
|
1611
|
-
* @param options.debounce - Milliseconds to wait before sending the latest request.
|
|
1612
|
-
* @param options.baseUrl - Base URL override used for this hook instance.
|
|
1613
|
-
* @returns Streaming request state and helper methods described by {@link UseFaasStreamResult}.
|
|
1684
|
+
* @param {string} action - Action path to invoke.
|
|
1685
|
+
* @param {Record<string, any>} defaultParams - Params used for the initial request and future reloads.
|
|
1686
|
+
* @param {UseFaasStreamOptions} [options] - Optional hook configuration such as controlled stream text, skip logic, debounce timing, and base URL overrides.
|
|
1687
|
+
* See the `UseFaasStreamOptions` type for `params`, `data`, `setData`, `skip`, `debounce`, and `baseUrl`.
|
|
1688
|
+
* @returns {UseFaasStreamResult} Streaming request state and helper methods described by {@link UseFaasStreamResult}.
|
|
1614
1689
|
*
|
|
1615
1690
|
* @example
|
|
1616
1691
|
* ```tsx
|
|
@@ -1637,123 +1712,46 @@ function createSplittingContext(defaultValue) {
|
|
|
1637
1712
|
* ```
|
|
1638
1713
|
*/
|
|
1639
1714
|
function useFaasStream(action, defaultParams, options = {}) {
|
|
1640
|
-
const [loading, setLoading] = useState(true);
|
|
1641
1715
|
const [data, setData] = useState(options.data || "");
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
useEqualEffect(() => {
|
|
1651
|
-
setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
|
|
1652
|
-
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
1653
|
-
useEqualEffect(() => {
|
|
1654
|
-
if (!equal(defaultParams, params)) setParams(defaultParams);
|
|
1655
|
-
}, [defaultParams]);
|
|
1656
|
-
useEqualEffect(() => {
|
|
1657
|
-
if (!action || skip) {
|
|
1658
|
-
setLoading(false);
|
|
1659
|
-
return;
|
|
1660
|
-
}
|
|
1661
|
-
setLoading(true);
|
|
1662
|
-
setData("");
|
|
1663
|
-
const controller = new AbortController();
|
|
1664
|
-
controllerRef.current = controller;
|
|
1665
|
-
const client = getClient(options.baseUrl);
|
|
1666
|
-
const requestParams = options.params ?? params;
|
|
1667
|
-
function send() {
|
|
1668
|
-
client.browserClient.action(action, requestParams, {
|
|
1669
|
-
signal: controller.signal,
|
|
1716
|
+
const request = useFaasRequest({
|
|
1717
|
+
action,
|
|
1718
|
+
defaultParams,
|
|
1719
|
+
options,
|
|
1720
|
+
beforeSend: () => setData(""),
|
|
1721
|
+
send: async ({ action, params, signal, client }) => {
|
|
1722
|
+
const response = await client.browserClient.action(action, params, {
|
|
1723
|
+
signal,
|
|
1670
1724
|
stream: true
|
|
1671
|
-
}).then(async (response) => {
|
|
1672
|
-
if (!response.body) {
|
|
1673
|
-
setError(/* @__PURE__ */ new Error("Response body is null"));
|
|
1674
|
-
setLoading(false);
|
|
1675
|
-
return;
|
|
1676
|
-
}
|
|
1677
|
-
const reader = response.body.getReader();
|
|
1678
|
-
const decoder = new TextDecoder();
|
|
1679
|
-
let accumulatedText = "";
|
|
1680
|
-
try {
|
|
1681
|
-
while (true) {
|
|
1682
|
-
const { done, value } = await reader.read();
|
|
1683
|
-
if (done) break;
|
|
1684
|
-
accumulatedText += decoder.decode(value, { stream: true });
|
|
1685
|
-
setData(accumulatedText);
|
|
1686
|
-
}
|
|
1687
|
-
setFails(0);
|
|
1688
|
-
setError(null);
|
|
1689
|
-
setLoading(false);
|
|
1690
|
-
for (const { resolve } of pendingReloadsRef.current.values()) resolve(accumulatedText);
|
|
1691
|
-
pendingReloadsRef.current.clear();
|
|
1692
|
-
} catch (readError) {
|
|
1693
|
-
reader.releaseLock();
|
|
1694
|
-
throw readError;
|
|
1695
|
-
}
|
|
1696
|
-
}).catch(async (e) => {
|
|
1697
|
-
if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0) return;
|
|
1698
|
-
if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
|
|
1699
|
-
console.warn(`FaasReactClient: ${e.message} retry...`);
|
|
1700
|
-
setFails(1);
|
|
1701
|
-
return send();
|
|
1702
|
-
}
|
|
1703
|
-
let error = e;
|
|
1704
|
-
if (client.onError) try {
|
|
1705
|
-
await client.onError(action, requestParams)(e);
|
|
1706
|
-
} catch (newError) {
|
|
1707
|
-
error = newError;
|
|
1708
|
-
}
|
|
1709
|
-
setError(error);
|
|
1710
|
-
setLoading(false);
|
|
1711
|
-
for (const { reject } of pendingReloadsRef.current.values()) reject(error);
|
|
1712
|
-
pendingReloadsRef.current.clear();
|
|
1713
1725
|
});
|
|
1726
|
+
if (!response.body) throw new Error("Response body is null");
|
|
1727
|
+
const reader = response.body.getReader();
|
|
1728
|
+
const decoder = new TextDecoder();
|
|
1729
|
+
let accumulatedText = "";
|
|
1730
|
+
try {
|
|
1731
|
+
while (true) {
|
|
1732
|
+
const { done, value } = await reader.read();
|
|
1733
|
+
if (done) break;
|
|
1734
|
+
accumulatedText += decoder.decode(value, { stream: true });
|
|
1735
|
+
setData(accumulatedText);
|
|
1736
|
+
}
|
|
1737
|
+
return accumulatedText;
|
|
1738
|
+
} catch (error) {
|
|
1739
|
+
reader.releaseLock();
|
|
1740
|
+
throw error;
|
|
1741
|
+
}
|
|
1714
1742
|
}
|
|
1715
|
-
|
|
1716
|
-
const timeout = setTimeout(send, options.debounce);
|
|
1717
|
-
return () => {
|
|
1718
|
-
clearTimeout(timeout);
|
|
1719
|
-
controllerRef.current?.abort();
|
|
1720
|
-
setLoading(false);
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
send();
|
|
1724
|
-
return () => {
|
|
1725
|
-
controllerRef.current?.abort();
|
|
1726
|
-
setLoading(false);
|
|
1727
|
-
};
|
|
1728
|
-
}, [
|
|
1729
|
-
action,
|
|
1730
|
-
options.params || params,
|
|
1731
|
-
reloadTimes,
|
|
1732
|
-
skip
|
|
1733
|
-
]);
|
|
1734
|
-
const reload = useEqualCallback((params) => {
|
|
1735
|
-
if (skip) setSkip(false);
|
|
1736
|
-
if (params) setParams(params);
|
|
1737
|
-
const reloadCounter = ++reloadCounterRef.current;
|
|
1738
|
-
return new Promise((resolve, reject) => {
|
|
1739
|
-
pendingReloadsRef.current.set(reloadCounter, {
|
|
1740
|
-
resolve,
|
|
1741
|
-
reject
|
|
1742
|
-
});
|
|
1743
|
-
setReloadTimes((prev) => prev + 1);
|
|
1744
|
-
});
|
|
1745
|
-
}, [params, skip]);
|
|
1743
|
+
});
|
|
1746
1744
|
return {
|
|
1747
1745
|
action,
|
|
1748
|
-
params,
|
|
1749
|
-
loading,
|
|
1746
|
+
params: request.params,
|
|
1747
|
+
loading: request.loading,
|
|
1748
|
+
reloadTimes: request.reloadTimes,
|
|
1750
1749
|
data: options.data || data,
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
reload,
|
|
1750
|
+
error: request.error,
|
|
1751
|
+
reload: request.reload,
|
|
1754
1752
|
setData: options.setData || setData,
|
|
1755
|
-
setLoading,
|
|
1756
|
-
setError
|
|
1753
|
+
setLoading: request.setLoading,
|
|
1754
|
+
setError: request.setError
|
|
1757
1755
|
};
|
|
1758
1756
|
}
|
|
1759
1757
|
//#endregion
|
|
@@ -1762,8 +1760,8 @@ function useFaasStream(action, defaultParams, options = {}) {
|
|
|
1762
1760
|
* Hook to store the previous value of a state or prop.
|
|
1763
1761
|
*
|
|
1764
1762
|
* @template T - The type of the value.
|
|
1765
|
-
* @param value - The current value to track.
|
|
1766
|
-
* @returns Previous value from the prior render, or `undefined` on the first render.
|
|
1763
|
+
* @param {T} value - The current value to track.
|
|
1764
|
+
* @returns {T | undefined} Previous value from the prior render, or `undefined` on the first render.
|
|
1767
1765
|
*
|
|
1768
1766
|
* @example
|
|
1769
1767
|
* ```tsx
|
|
@@ -1789,8 +1787,8 @@ function usePrevious(value) {
|
|
|
1789
1787
|
* Custom hook that returns a stateful value and a ref to that value.
|
|
1790
1788
|
*
|
|
1791
1789
|
* @template T - The type of the value.
|
|
1792
|
-
* @param initialValue - Initial state value. When omitted, state starts as `null`.
|
|
1793
|
-
* @returns Tuple containing the current state, the state setter, and a ref that always points at the latest state.
|
|
1790
|
+
* @param {T} [initialValue] - Initial state value. When omitted, state starts as `null`.
|
|
1791
|
+
* @returns {[T | null, Dispatch<SetStateAction<T | null>>, RefObject<T | null>]} Tuple containing the current state, the state setter, and a ref that always points at the latest state.
|
|
1794
1792
|
*
|
|
1795
1793
|
* @example
|
|
1796
1794
|
* ```tsx
|