@faasjs/react 8.0.0-beta.20 → 8.0.0-beta.21

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 CHANGED
@@ -1091,25 +1091,23 @@ declare const FaasDataWrapper: <PathOrData extends FaasActionUnionType$1 = any>(
1091
1091
  */
1092
1092
  declare function withFaasData<PathOrData extends FaasActionUnionType$1, TComponentProps extends Required<FaasDataInjection<PathOrData>> = Required<FaasDataInjection<PathOrData>>>(Component: React.FC<TComponentProps>, faasProps: FaasDataWrapperProps<PathOrData>): React.FC<Omit<TComponentProps, keyof FaasDataInjection<PathOrData>> & Record<string, any>>;
1093
1093
  //#endregion
1094
+ //#region src/useFaasRequest.d.ts
1095
+ type SharedUseFaasOptions<Params, Data> = {
1096
+ params?: Params;
1097
+ data?: Data;
1098
+ setData?: React.Dispatch<React.SetStateAction<Data>>;
1099
+ skip?: boolean | ((params: Params) => boolean);
1100
+ debounce?: number;
1101
+ baseUrl?: BaseUrl;
1102
+ };
1103
+ //#endregion
1094
1104
  //#region src/useFaas.d.ts
1095
1105
  /**
1096
1106
  * Options that customize the {@link useFaas} request lifecycle.
1097
1107
  *
1098
1108
  * @template PathOrData - Action path or response data type used for inference.
1099
1109
  */
1100
- type useFaasOptions<PathOrData extends FaasActionUnionType$1> = {
1101
- /** Override the current request params without changing the hook's stored params state. */params?: FaasParams$1<PathOrData>; /** Controlled data value used instead of the hook's internal state. */
1102
- data?: FaasData$1<PathOrData>; /** Controlled setter that is called instead of the hook's internal `setData`. */
1103
- setData?: React.Dispatch<React.SetStateAction<FaasData$1<PathOrData>>>;
1104
- /**
1105
- * If skip is true, the request will not be sent.
1106
- *
1107
- * However, you can still use reload to send the request.
1108
- */
1109
- skip?: boolean | ((params: FaasParams$1<PathOrData>) => boolean); /** Delay the latest automatic request by the given number of milliseconds. */
1110
- debounce?: number; /** Override the default base URL for this hook instance. */
1111
- baseUrl?: BaseUrl;
1112
- };
1110
+ type useFaasOptions<PathOrData extends FaasActionUnionType$1> = SharedUseFaasOptions<FaasParams$1<PathOrData>, FaasData$1<PathOrData>>;
1113
1111
  /**
1114
1112
  * Request FaasJS data and keep request state in React state.
1115
1113
  *
@@ -1359,7 +1357,7 @@ declare class ErrorBoundary extends Component<ErrorBoundaryProps, {
1359
1357
  /**
1360
1358
  * Render children or the configured fallback for the captured error.
1361
1359
  */
1362
- render(): string | number | bigint | boolean | _$react_jsx_runtime0.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
1360
+ render(): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | _$react_jsx_runtime0.JSX.Element | null;
1363
1361
  }
1364
1362
  //#endregion
1365
1363
  //#region src/equal.d.ts
@@ -1506,7 +1504,7 @@ type OptionalWrapperProps<TWrapper extends ComponentType<{
1506
1504
  * )
1507
1505
  * ```
1508
1506
  */
1509
- declare function OptionalWrapper(props: OptionalWrapperProps): string | number | bigint | boolean | _$react_jsx_runtime0.JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
1507
+ declare function OptionalWrapper(props: OptionalWrapperProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | _$react.ReactPortal | _$react.ReactElement<unknown, string | _$react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | _$react_jsx_runtime0.JSX.Element | null | undefined;
1510
1508
  declare namespace OptionalWrapper {
1511
1509
  var displayName: string;
1512
1510
  }
@@ -1662,19 +1660,7 @@ declare function useSplittingState<T extends Record<string, unknown>>(initialSta
1662
1660
  /**
1663
1661
  * Options that customize the {@link useFaasStream} request lifecycle.
1664
1662
  */
1665
- type UseFaasStreamOptions = {
1666
- /** Override the current request params without changing the hook's stored params state. */params?: Record<string, any>; /** Controlled stream text used instead of the hook's internal state. */
1667
- data?: string; /** Controlled setter that is called instead of the hook's internal `setData`. */
1668
- setData?: React.Dispatch<React.SetStateAction<string>>;
1669
- /**
1670
- * If skip is true, the request will not be sent.
1671
- *
1672
- * However, you can still use reload to send the request.
1673
- */
1674
- skip?: boolean | ((params: Record<string, any>) => boolean); /** Delay the latest automatic request by the given number of milliseconds. */
1675
- debounce?: number; /** Override the default base URL for this hook instance. */
1676
- baseUrl?: BaseUrl;
1677
- };
1663
+ type UseFaasStreamOptions = SharedUseFaasOptions<Record<string, any>, string>;
1678
1664
  /**
1679
1665
  * Result returned by {@link useFaasStream}.
1680
1666
  */
package/dist/index.mjs CHANGED
@@ -1083,6 +1083,126 @@ function withFaasData(Component, faasProps) {
1083
1083
  });
1084
1084
  }
1085
1085
  //#endregion
1086
+ //#region src/useFaasRequest.ts
1087
+ function useFaasRequest({ action, defaultParams, options, beforeSend, onSuccess, send }) {
1088
+ const [loading, setLoading] = useState(true);
1089
+ const [error, setError] = useState();
1090
+ const [params, setParams] = useState(defaultParams);
1091
+ const [reloadTimes, setReloadTimes] = useState(0);
1092
+ const [skip, setSkip] = useState(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
1093
+ const promiseRef = useRef(null);
1094
+ const controllerRef = useRef(null);
1095
+ const failedOnceRef = useRef(false);
1096
+ const pendingReloadsRef = useRef(/* @__PURE__ */ new Map());
1097
+ const reloadCounterRef = useRef(0);
1098
+ const beforeSendRef = useRef(beforeSend);
1099
+ const onSuccessRef = useRef(onSuccess);
1100
+ const sendRef = useRef(send);
1101
+ beforeSendRef.current = beforeSend;
1102
+ onSuccessRef.current = onSuccess;
1103
+ sendRef.current = send;
1104
+ useEqualEffect(() => {
1105
+ setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
1106
+ }, [typeof options.skip === "function" ? params : options.skip]);
1107
+ useEqualEffect(() => {
1108
+ if (!equal(defaultParams, params)) setParams(defaultParams);
1109
+ }, [defaultParams]);
1110
+ useEqualEffect(() => {
1111
+ if (!action || skip) {
1112
+ setLoading(false);
1113
+ return;
1114
+ }
1115
+ setLoading(true);
1116
+ beforeSendRef.current?.();
1117
+ failedOnceRef.current = false;
1118
+ const controller = new AbortController();
1119
+ controllerRef.current = controller;
1120
+ const client = getClient(options.baseUrl);
1121
+ const requestParams = options.params || params;
1122
+ const rejectPending = (reason) => {
1123
+ for (const { reject } of pendingReloadsRef.current.values()) reject(reason);
1124
+ pendingReloadsRef.current.clear();
1125
+ };
1126
+ const resolvePending = (value) => {
1127
+ for (const { resolve } of pendingReloadsRef.current.values()) resolve(value);
1128
+ pendingReloadsRef.current.clear();
1129
+ };
1130
+ const run = () => {
1131
+ sendRef.current({
1132
+ action,
1133
+ params: requestParams,
1134
+ signal: controller.signal,
1135
+ client,
1136
+ setPromise: (promise) => {
1137
+ promiseRef.current = promise;
1138
+ }
1139
+ }).then((result) => {
1140
+ failedOnceRef.current = false;
1141
+ setError(null);
1142
+ onSuccessRef.current?.(result);
1143
+ setLoading(false);
1144
+ resolvePending(result);
1145
+ }).catch(async (e) => {
1146
+ if (typeof e?.message === "string" && e.message.toLowerCase().includes("aborted")) return;
1147
+ if (!failedOnceRef.current && typeof e?.message === "string" && e.message.includes("Failed to fetch")) {
1148
+ failedOnceRef.current = true;
1149
+ console.warn(`FaasReactClient: ${e.message} retry...`);
1150
+ run();
1151
+ return;
1152
+ }
1153
+ let nextError = e;
1154
+ if (client.onError) try {
1155
+ await client.onError(action, requestParams || Object.create(null))(e);
1156
+ } catch (newError) {
1157
+ nextError = newError;
1158
+ }
1159
+ setError(nextError);
1160
+ setLoading(false);
1161
+ rejectPending(nextError);
1162
+ });
1163
+ };
1164
+ if (options.debounce) {
1165
+ const timeout = setTimeout(run, options.debounce);
1166
+ return () => {
1167
+ clearTimeout(timeout);
1168
+ controllerRef.current?.abort();
1169
+ setLoading(false);
1170
+ };
1171
+ }
1172
+ run();
1173
+ return () => {
1174
+ controllerRef.current?.abort();
1175
+ setLoading(false);
1176
+ };
1177
+ }, [
1178
+ action,
1179
+ options.params || params,
1180
+ reloadTimes,
1181
+ skip
1182
+ ]);
1183
+ return {
1184
+ loading,
1185
+ error,
1186
+ params,
1187
+ reloadTimes,
1188
+ reload: useEqualCallback((nextParams) => {
1189
+ if (skip) setSkip(false);
1190
+ if (nextParams) setParams(nextParams);
1191
+ const reloadCounter = ++reloadCounterRef.current;
1192
+ return new Promise((resolve, reject) => {
1193
+ pendingReloadsRef.current.set(reloadCounter, {
1194
+ resolve,
1195
+ reject
1196
+ });
1197
+ setReloadTimes((prev) => prev + 1);
1198
+ });
1199
+ }, [skip]),
1200
+ promiseRef,
1201
+ setError,
1202
+ setLoading
1203
+ };
1204
+ }
1205
+ //#endregion
1086
1206
  //#region src/useFaas.tsx
1087
1207
  /**
1088
1208
  * Request FaasJS data and keep request state in React state.
@@ -1136,113 +1256,40 @@ function withFaasData(Component, faasProps) {
1136
1256
  * ```
1137
1257
  */
1138
1258
  function useFaas(action, defaultParams, options = {}) {
1139
- const [loading, setLoading] = useState(true);
1140
1259
  const [data, setData] = useState();
1141
- const [error, setError] = useState();
1142
- const [params, setParams] = useState(defaultParams);
1143
- const [reloadTimes, setReloadTimes] = useState(0);
1144
- const [fails, setFails] = useState(0);
1145
- const [skip, setSkip] = useState(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
1146
- const promiseRef = useRef(null);
1147
- const controllerRef = useRef(null);
1148
1260
  const localSetData = setData;
1149
- const pendingReloadsRef = useRef(/* @__PURE__ */ new Map());
1150
- const reloadCounterRef = useRef(0);
1151
- useEqualEffect(() => {
1152
- setSkip(typeof options.skip === "function" ? options.skip(params) : options.skip);
1153
- }, [typeof options.skip === "function" ? params : options.skip]);
1154
- useEqualEffect(() => {
1155
- if (!equal(defaultParams, params)) setParams(defaultParams);
1156
- }, [defaultParams]);
1157
- useEqualEffect(() => {
1158
- if (!action || skip) {
1159
- setLoading(false);
1160
- return;
1161
- }
1162
- setLoading(true);
1163
- const controller = new AbortController();
1164
- controllerRef.current = controller;
1165
- const client = getClient(options.baseUrl);
1166
- const requestParams = options.params ?? params;
1167
- function send() {
1168
- const request = client.faas(action, requestParams, { signal: controller.signal });
1169
- promiseRef.current = request;
1170
- request.then((r) => {
1171
- const nextData = r.data;
1172
- setFails(0);
1173
- setError(null);
1174
- if (options.setData) options.setData(nextData);
1175
- else localSetData(nextData);
1176
- setLoading(false);
1177
- for (const { resolve } of pendingReloadsRef.current.values()) resolve(nextData);
1178
- pendingReloadsRef.current.clear();
1179
- }).catch(async (e) => {
1180
- if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0) return;
1181
- if (!fails && typeof e?.message === "string" && e.message.indexOf("Failed to fetch") >= 0) {
1182
- console.warn(`FaasReactClient: ${e.message} retry...`);
1183
- setFails(1);
1184
- return send();
1185
- }
1186
- let error = e;
1187
- if (client.onError) try {
1188
- await client.onError(action, requestParams)(e);
1189
- } catch (newError) {
1190
- error = newError;
1191
- }
1192
- setError(error);
1193
- setLoading(false);
1194
- for (const { reject } of pendingReloadsRef.current.values()) reject(error);
1195
- pendingReloadsRef.current.clear();
1196
- });
1197
- }
1198
- if (options.debounce) {
1199
- const timeout = setTimeout(send, options.debounce);
1200
- return () => {
1201
- clearTimeout(timeout);
1202
- controllerRef.current?.abort();
1203
- setLoading(false);
1204
- };
1205
- }
1206
- send();
1207
- return () => {
1208
- controllerRef.current?.abort();
1209
- setLoading(false);
1210
- };
1211
- }, [
1261
+ const request = useFaasRequest({
1212
1262
  action,
1213
- options.params || params,
1214
- reloadTimes,
1215
- skip
1216
- ]);
1217
- const reload = useEqualCallback((params) => {
1218
- if (skip) setSkip(false);
1219
- if (params) setParams(params);
1220
- const reloadCounter = ++reloadCounterRef.current;
1221
- return new Promise((resolve, reject) => {
1222
- pendingReloadsRef.current.set(reloadCounter, {
1223
- resolve,
1224
- reject
1225
- });
1226
- setReloadTimes((prev) => prev + 1);
1227
- });
1228
- }, [params, skip]);
1263
+ defaultParams,
1264
+ options,
1265
+ onSuccess: (nextData) => {
1266
+ if (options.setData) options.setData(nextData);
1267
+ else localSetData(nextData);
1268
+ },
1269
+ send: ({ action, params, signal, client, setPromise }) => {
1270
+ const promise = client.faas(action, params, { signal });
1271
+ setPromise(promise);
1272
+ return promise.then((response) => response.data);
1273
+ }
1274
+ });
1229
1275
  const currentData = options.data ?? data;
1230
- const currentPromise = promiseRef.current ?? Promise.resolve({});
1276
+ const currentPromise = request.promiseRef.current ?? Promise.resolve({});
1277
+ const updateData = options.setData ?? localSetData;
1231
1278
  return {
1232
1279
  action,
1233
- params,
1234
- loading,
1280
+ params: request.params,
1281
+ loading: request.loading,
1235
1282
  data: currentData,
1236
- reloadTimes,
1237
- error,
1283
+ reloadTimes: request.reloadTimes,
1284
+ error: request.error,
1238
1285
  promise: currentPromise,
1239
- reload,
1240
- setData: options.setData ?? localSetData,
1241
- setLoading,
1286
+ reload: request.reload,
1287
+ setData: updateData,
1288
+ setLoading: request.setLoading,
1242
1289
  setPromise: (newPromise) => {
1243
- promiseRef.current = typeof newPromise === "function" ? newPromise(currentPromise) : newPromise;
1290
+ request.promiseRef.current = typeof newPromise === "function" ? newPromise(currentPromise) : newPromise;
1244
1291
  },
1245
- setError
1292
+ setError: request.setError
1246
1293
  };
1247
1294
  }
1248
1295
  //#endregion
@@ -1637,123 +1684,46 @@ function createSplittingContext(defaultValue) {
1637
1684
  * ```
1638
1685
  */
1639
1686
  function useFaasStream(action, defaultParams, options = {}) {
1640
- const [loading, setLoading] = useState(true);
1641
1687
  const [data, setData] = useState(options.data || "");
1642
- const [error, setError] = useState();
1643
- const [params, setParams] = useState(defaultParams);
1644
- const [reloadTimes, setReloadTimes] = useState(0);
1645
- const [fails, setFails] = useState(0);
1646
- const [skip, setSkip] = useState(typeof options.skip === "function" ? options.skip(defaultParams) : options.skip);
1647
- const controllerRef = useRef(null);
1648
- const pendingReloadsRef = useRef(/* @__PURE__ */ new Map());
1649
- const reloadCounterRef = useRef(0);
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,
1688
+ const request = useFaasRequest({
1689
+ action,
1690
+ defaultParams,
1691
+ options,
1692
+ beforeSend: () => setData(""),
1693
+ send: async ({ action, params, signal, client }) => {
1694
+ const response = await client.browserClient.action(action, params, {
1695
+ signal,
1670
1696
  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
1697
  });
1698
+ if (!response.body) throw new Error("Response body is null");
1699
+ const reader = response.body.getReader();
1700
+ const decoder = new TextDecoder();
1701
+ let accumulatedText = "";
1702
+ try {
1703
+ while (true) {
1704
+ const { done, value } = await reader.read();
1705
+ if (done) break;
1706
+ accumulatedText += decoder.decode(value, { stream: true });
1707
+ setData(accumulatedText);
1708
+ }
1709
+ return accumulatedText;
1710
+ } catch (error) {
1711
+ reader.releaseLock();
1712
+ throw error;
1713
+ }
1714
1714
  }
1715
- if (options.debounce) {
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]);
1715
+ });
1746
1716
  return {
1747
1717
  action,
1748
- params,
1749
- loading,
1718
+ params: request.params,
1719
+ loading: request.loading,
1720
+ reloadTimes: request.reloadTimes,
1750
1721
  data: options.data || data,
1751
- reloadTimes,
1752
- error,
1753
- reload,
1722
+ error: request.error,
1723
+ reload: request.reload,
1754
1724
  setData: options.setData || setData,
1755
- setLoading,
1756
- setError
1725
+ setLoading: request.setLoading,
1726
+ setError: request.setError
1757
1727
  };
1758
1728
  }
1759
1729
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "8.0.0-beta.20",
3
+ "version": "8.0.0-beta.21",
4
4
  "homepage": "https://faasjs.com/doc/react/",
5
5
  "bugs": {
6
6
  "url": "https://github.com/faasjs/faasjs/issues"
@@ -26,12 +26,12 @@
26
26
  }
27
27
  },
28
28
  "devDependencies": {
29
- "@faasjs/types": ">=8.0.0-beta.20",
29
+ "@faasjs/types": ">=8.0.0-beta.21",
30
30
  "@types/react": "^19.0.0",
31
31
  "react": "^19.0.0"
32
32
  },
33
33
  "peerDependencies": {
34
- "@faasjs/types": ">=8.0.0-beta.20"
34
+ "@faasjs/types": ">=8.0.0-beta.21"
35
35
  },
36
36
  "engines": {
37
37
  "node": ">=24.0.0",