@faasjs/react 8.0.0-beta.14 → 8.0.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -41
- package/dist/index.cjs +147 -114
- package/dist/index.d.ts +145 -110
- package/dist/index.mjs +147 -114
- package/package.json +3 -6
package/README.md
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
1
|
# @faasjs/react
|
|
2
2
|
|
|
3
|
-
React plugin for FaasJS.
|
|
4
|
-
|
|
5
|
-
[](https://github.com/faasjs/faasjs/blob/main/packages/react/LICENSE)
|
|
6
|
-
[](https://www.npmjs.com/package/@faasjs/react)
|
|
7
|
-
|
|
8
|
-
Includes browser client utilities (`FaasBrowserClient`, `ResponseError`, `setMock`) and React helpers.
|
|
9
|
-
|
|
10
|
-
## Features
|
|
11
|
-
|
|
12
|
-
- Support [FaasJS Request Specifications](https://faasjs.com/guide/request-spec.html).
|
|
13
|
-
- Support global and per-request configurations.
|
|
14
|
-
- Compatible with [why-did-you-render](https://github.com/welldone-software/why-did-you-render).
|
|
15
|
-
- Additional React functions:
|
|
16
|
-
- Utils:
|
|
17
|
-
- `equal`: Compare two values for deep equality.
|
|
18
|
-
- `createSplittingContext`: Create a context for code splitting.
|
|
19
|
-
- `useSplittingState`: Create splitting states.
|
|
20
|
-
- Hooks:
|
|
21
|
-
- `useEqualMemoize`: Memoize a value with deep equality.
|
|
22
|
-
- `useEqualEffect`: Run an effect with deep equality.
|
|
23
|
-
- `useEqualMemo`: Memoize a value with deep equality.
|
|
24
|
-
- `useEqualCallback`: Memoize a callback with deep equality.
|
|
25
|
-
- `useConstant`: Create a constant value with hooks.
|
|
26
|
-
- `usePrevious`: Get the previous value of a state.
|
|
27
|
-
- `useStateRef`: Create a state with a ref.
|
|
28
|
-
- Components:
|
|
29
|
-
- `OptionalWrapper`: Render a component optionally.
|
|
30
|
-
- `ErrorBoundary`: Catch errors in the component tree.
|
|
31
|
-
- Fetch Data:
|
|
32
|
-
- `faas`: Fetch data from FaasJS.
|
|
33
|
-
- `useFaas`: Fetch data from FaasJS with hooks.
|
|
34
|
-
- `useFaasStream`: Fetch streaming data from FaasJS with hooks.
|
|
35
|
-
- `FaasDataWrapper`: Fetch data from FaasJS with a wrapper component.
|
|
36
|
-
- `withFaasData`: Fetch data from FaasJS using a higher-order component (HOC).
|
|
37
|
-
|
|
38
|
-
## Install
|
|
39
|
-
|
|
40
|
-
```sh
|
|
41
|
-
npm install @faasjs/react react
|
|
42
|
-
```
|
|
43
|
-
|
|
44
3
|
## Functions
|
|
45
4
|
|
|
46
5
|
- [createSplittingContext](functions/createSplittingContext.md)
|
package/dist/index.cjs
CHANGED
|
@@ -36,10 +36,7 @@ function generateId(prefix = "", length = 18) {
|
|
|
36
36
|
* @property {T} [data] - The parsed JSON data from the response.
|
|
37
37
|
* Optional property that contains the response payload when JSON is provided.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
* All properties are optional with sensible defaults.
|
|
41
|
-
*
|
|
42
|
-
* @remarks
|
|
39
|
+
* Notes:
|
|
43
40
|
* - status defaults to 200 if data or body is present, 204 otherwise
|
|
44
41
|
* - body is automatically populated from data if not explicitly provided
|
|
45
42
|
* - headers defaults to an empty object if not provided
|
|
@@ -142,6 +139,12 @@ var Response = class {
|
|
|
142
139
|
headers;
|
|
143
140
|
body;
|
|
144
141
|
data;
|
|
142
|
+
/**
|
|
143
|
+
* Create a wrapped response object.
|
|
144
|
+
*
|
|
145
|
+
* @param props - Response properties including status, headers, body, and data.
|
|
146
|
+
* @returns Wrapped response instance.
|
|
147
|
+
*/
|
|
145
148
|
constructor(props = {}) {
|
|
146
149
|
this.status = props.status || (props.data || props.body ? 200 : 204);
|
|
147
150
|
this.headers = props.headers || {};
|
|
@@ -156,17 +159,13 @@ var Response = class {
|
|
|
156
159
|
* Extends the built-in Error class to provide additional information about failed requests,
|
|
157
160
|
* including HTTP status code, response headers, response body, and the original error.
|
|
158
161
|
*
|
|
159
|
-
* @
|
|
160
|
-
* @extends {Error}
|
|
162
|
+
* @augments Error
|
|
161
163
|
*
|
|
162
164
|
* @property {number} status - The HTTP status code of the failed response. Defaults to 500 if not provided.
|
|
163
165
|
* @property {ResponseHeaders} headers - The response headers from the failed request.
|
|
164
166
|
* @property {any} body - The response body containing error details or the original error if available.
|
|
165
167
|
* @property {Error} [originalError] - The original Error object if this ResponseError was created from another Error.
|
|
166
168
|
*
|
|
167
|
-
* @param {string | Error | ResponseErrorProps} data - The error message, an Error object, or a ResponseErrorProps object.
|
|
168
|
-
* @param {Omit<ResponseErrorProps, 'message' | 'originalError'>} [options] - Additional options for the error (status, headers, body).
|
|
169
|
-
*
|
|
170
169
|
* @example Basic error with message
|
|
171
170
|
* ```ts
|
|
172
171
|
* throw new ResponseError('User not found')
|
|
@@ -238,7 +237,7 @@ var Response = class {
|
|
|
238
237
|
* })
|
|
239
238
|
* ```
|
|
240
239
|
*
|
|
241
|
-
*
|
|
240
|
+
* Notes:
|
|
242
241
|
* - ResponseError is automatically thrown by the action method when the server returns an error (status >= 400)
|
|
243
242
|
* - The error message from server responses is extracted from body.error.message if available
|
|
244
243
|
* - When created from an Error object, the original error is preserved in the originalError property
|
|
@@ -255,13 +254,20 @@ var ResponseError = class extends Error {
|
|
|
255
254
|
headers;
|
|
256
255
|
body;
|
|
257
256
|
originalError;
|
|
257
|
+
/**
|
|
258
|
+
* Create a ResponseError from a message, Error, or structured response error payload.
|
|
259
|
+
*
|
|
260
|
+
* @param data - Error message, Error object, or structured response error props.
|
|
261
|
+
* @param options - Additional options such as status, headers, and body.
|
|
262
|
+
* @returns ResponseError instance.
|
|
263
|
+
*/
|
|
258
264
|
constructor(data, options) {
|
|
259
265
|
let props;
|
|
260
266
|
if (typeof data === "string") props = {
|
|
261
267
|
message: data,
|
|
262
268
|
...options
|
|
263
269
|
};
|
|
264
|
-
else if (data instanceof Error || data
|
|
270
|
+
else if (data instanceof Error || typeof data === "object" && data !== null && typeof data.constructor?.name === "string" && data.constructor.name.includes("Error")) props = {
|
|
265
271
|
message: data.message,
|
|
266
272
|
originalError: data,
|
|
267
273
|
...options
|
|
@@ -344,7 +350,7 @@ function setMock(handler) {
|
|
|
344
350
|
* - Streaming support for large responses
|
|
345
351
|
* - Multiple instance support with unique IDs
|
|
346
352
|
*
|
|
347
|
-
*
|
|
353
|
+
* Notes:
|
|
348
354
|
* - All requests are POST requests by default
|
|
349
355
|
* - Automatically adds X-FaasJS-Request-Id header for request tracking
|
|
350
356
|
* - baseUrl must end with '/' (will throw Error if not)
|
|
@@ -406,11 +412,8 @@ var FaasBrowserClient = class {
|
|
|
406
412
|
/**
|
|
407
413
|
* Creates a new FaasBrowserClient instance.
|
|
408
414
|
*
|
|
409
|
-
* @param baseUrl - Base URL for all API requests. Must end with
|
|
410
|
-
*
|
|
411
|
-
* @param options - Configuration options for the client.
|
|
412
|
-
* Supports default headers, beforeRequest hook, custom request function,
|
|
413
|
-
* baseUrl override, and streaming mode.
|
|
415
|
+
* @param baseUrl - Base URL for all API requests. Must end with `/`. Defaults to `/` for relative requests.
|
|
416
|
+
* @param options - Default request options such as headers, hooks, request override, or stream mode.
|
|
414
417
|
*
|
|
415
418
|
* @example Basic initialization
|
|
416
419
|
* ```ts
|
|
@@ -461,7 +464,7 @@ var FaasBrowserClient = class {
|
|
|
461
464
|
* })
|
|
462
465
|
* ```
|
|
463
466
|
*
|
|
464
|
-
* @throws {Error} When baseUrl does not end with
|
|
467
|
+
* @throws {Error} When `baseUrl` does not end with `/`
|
|
465
468
|
*/
|
|
466
469
|
constructor(baseUrl = "/", options = Object.create(null)) {
|
|
467
470
|
if (baseUrl && !baseUrl.endsWith("/")) throw Error("[FaasJS] baseUrl should end with /");
|
|
@@ -486,9 +489,9 @@ var FaasBrowserClient = class {
|
|
|
486
489
|
*
|
|
487
490
|
* @throws {Error} When action is not provided or is empty
|
|
488
491
|
* @throws {ResponseError} When the server returns an error response (status >= 400 or body.error exists)
|
|
489
|
-
* @throws {
|
|
492
|
+
* @throws {Error} When the request fails before a response is received
|
|
490
493
|
*
|
|
491
|
-
*
|
|
494
|
+
* Notes:
|
|
492
495
|
* - All requests are POST requests by default
|
|
493
496
|
* - Action path is automatically converted to lowercase
|
|
494
497
|
* - A unique request ID is generated for each request and sent in X-FaasJS-Request-Id header
|
|
@@ -540,11 +543,7 @@ var FaasBrowserClient = class {
|
|
|
540
543
|
* email: string
|
|
541
544
|
* }
|
|
542
545
|
*
|
|
543
|
-
* const response = await client.action<{
|
|
544
|
-
* action: 'user'
|
|
545
|
-
* params: { id: number }
|
|
546
|
-
* data: UserData
|
|
547
|
-
* }>('user', { id: 123 })
|
|
546
|
+
* const response = await client.action<UserData>('user', { id: 123 })
|
|
548
547
|
* console.log(response.data.name) // TypeScript knows it's a string
|
|
549
548
|
* ```
|
|
550
549
|
*
|
|
@@ -556,7 +555,7 @@ var FaasBrowserClient = class {
|
|
|
556
555
|
* } catch (error) {
|
|
557
556
|
* if (error instanceof ResponseError) {
|
|
558
557
|
* console.error(`Server error: ${error.message}`, error.status)
|
|
559
|
-
* if (error.
|
|
558
|
+
* if (error.body) console.error('Error details:', error.body)
|
|
560
559
|
* } else {
|
|
561
560
|
* console.error('Network error:', error)
|
|
562
561
|
* }
|
|
@@ -647,7 +646,7 @@ var FaasBrowserClient = class {
|
|
|
647
646
|
headers,
|
|
648
647
|
body
|
|
649
648
|
}));
|
|
650
|
-
} catch
|
|
649
|
+
} catch {
|
|
651
650
|
return Promise.reject(new ResponseError({
|
|
652
651
|
message: res,
|
|
653
652
|
status: response.status,
|
|
@@ -662,17 +661,20 @@ var FaasBrowserClient = class {
|
|
|
662
661
|
//#endregion
|
|
663
662
|
//#region src/faas.ts
|
|
664
663
|
/**
|
|
665
|
-
*
|
|
664
|
+
* Call the currently configured FaasReactClient.
|
|
666
665
|
*
|
|
667
|
-
* @param action
|
|
668
|
-
* @param params
|
|
669
|
-
* @
|
|
666
|
+
* @param action - Action path to invoke.
|
|
667
|
+
* @param params - Parameters sent to the action.
|
|
668
|
+
* @param options - Optional per-request overrides such as headers or base URL.
|
|
669
|
+
* @returns Response returned by the active browser client.
|
|
670
670
|
*
|
|
671
671
|
* @example
|
|
672
672
|
* ```ts
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
* })
|
|
673
|
+
* import { faas } from '@faasjs/react'
|
|
674
|
+
*
|
|
675
|
+
* const response = await faas<{ title: string }>('post/get', { id: 1 })
|
|
676
|
+
*
|
|
677
|
+
* console.log(response.data.title)
|
|
676
678
|
* ```
|
|
677
679
|
*/
|
|
678
680
|
async function faas(action, params, options) {
|
|
@@ -733,13 +735,18 @@ function equal(a, b) {
|
|
|
733
735
|
* @returns The memoized value.
|
|
734
736
|
*/
|
|
735
737
|
function useEqualMemoize(value) {
|
|
738
|
+
const ref = (0, react.useRef)(value);
|
|
739
|
+
if (!equal(value, ref.current)) ref.current = value;
|
|
740
|
+
return ref.current;
|
|
741
|
+
}
|
|
742
|
+
function useEqualSignal(value) {
|
|
736
743
|
const ref = (0, react.useRef)(value);
|
|
737
744
|
const signalRef = (0, react.useRef)(0);
|
|
738
745
|
if (!equal(value, ref.current)) {
|
|
739
746
|
ref.current = value;
|
|
740
747
|
signalRef.current += 1;
|
|
741
748
|
}
|
|
742
|
-
return
|
|
749
|
+
return signalRef.current;
|
|
743
750
|
}
|
|
744
751
|
/**
|
|
745
752
|
* Custom hook that works like `useEffect` but uses deep comparison on dependencies.
|
|
@@ -749,7 +756,7 @@ function useEqualMemoize(value) {
|
|
|
749
756
|
* @returns The result of the `useEffect` hook with memoized dependencies.
|
|
750
757
|
*/
|
|
751
758
|
function useEqualEffect(callback, dependencies) {
|
|
752
|
-
return (0, react.useEffect)(callback,
|
|
759
|
+
return (0, react.useEffect)(callback, [useEqualSignal(dependencies)]);
|
|
753
760
|
}
|
|
754
761
|
/**
|
|
755
762
|
* Custom hook that works like `useMemo` but uses deep comparison on dependencies.
|
|
@@ -759,7 +766,12 @@ function useEqualEffect(callback, dependencies) {
|
|
|
759
766
|
* @returns The result of the `useMemo` hook with memoized dependencies.
|
|
760
767
|
*/
|
|
761
768
|
function useEqualMemo(callback, dependencies) {
|
|
762
|
-
|
|
769
|
+
const signal = useEqualSignal(dependencies);
|
|
770
|
+
const callbackRef = (0, react.useRef)(callback);
|
|
771
|
+
callbackRef.current = callback;
|
|
772
|
+
return (0, react.useMemo)(() => {
|
|
773
|
+
return callbackRef.current();
|
|
774
|
+
}, [signal]);
|
|
763
775
|
}
|
|
764
776
|
/**
|
|
765
777
|
* Custom hook that works like `useCallback` but uses deep comparison on dependencies.
|
|
@@ -769,7 +781,7 @@ function useEqualMemo(callback, dependencies) {
|
|
|
769
781
|
* @returns The result of the `useCallback` hook with memoized dependencies.
|
|
770
782
|
*/
|
|
771
783
|
function useEqualCallback(callback, dependencies) {
|
|
772
|
-
return (0, react.useCallback)((...args) => callback(...args),
|
|
784
|
+
return (0, react.useCallback)((...args) => callback(...args), [useEqualSignal(dependencies)]);
|
|
773
785
|
}
|
|
774
786
|
//#endregion
|
|
775
787
|
//#region src/FaasDataWrapper.tsx
|
|
@@ -821,16 +833,23 @@ function withFaasData(Component, faasProps) {
|
|
|
821
833
|
//#endregion
|
|
822
834
|
//#region src/useFaas.tsx
|
|
823
835
|
/**
|
|
824
|
-
* Request
|
|
836
|
+
* Request FaasJS data and keep request state in React state.
|
|
837
|
+
*
|
|
838
|
+
* `useFaas` sends an initial request unless `skip` is enabled, and returns
|
|
839
|
+
* request state plus helpers for reloading, updating data, and handling errors.
|
|
825
840
|
*
|
|
826
|
-
* @param action
|
|
827
|
-
* @param defaultParams
|
|
828
|
-
* @
|
|
841
|
+
* @param action - Action path to invoke.
|
|
842
|
+
* @param defaultParams - Params used for the initial request and future reloads.
|
|
843
|
+
* @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
|
|
844
|
+
* @returns Request state and helper methods for the action.
|
|
829
845
|
*
|
|
830
846
|
* @example
|
|
831
847
|
* ```tsx
|
|
832
|
-
*
|
|
848
|
+
* import { useFaas } from '@faasjs/react'
|
|
849
|
+
*
|
|
850
|
+
* function Post({ id }: { id: number }) {
|
|
833
851
|
* const { data } = useFaas<{ title: string }>('post/get', { id })
|
|
852
|
+
*
|
|
834
853
|
* return <h1>{data.title}</h1>
|
|
835
854
|
* }
|
|
836
855
|
* ```
|
|
@@ -871,7 +890,8 @@ function useFaas(action, defaultParams, options = {}) {
|
|
|
871
890
|
const nextData = r.data;
|
|
872
891
|
setFails(0);
|
|
873
892
|
setError(null);
|
|
874
|
-
options.setData
|
|
893
|
+
if (options.setData) options.setData(nextData);
|
|
894
|
+
else localSetData(nextData);
|
|
875
895
|
setLoading(false);
|
|
876
896
|
for (const { resolve } of pendingReloadsRef.current.values()) resolve(nextData);
|
|
877
897
|
pendingReloadsRef.current.clear();
|
|
@@ -949,14 +969,20 @@ function useFaas(action, defaultParams, options = {}) {
|
|
|
949
969
|
//#region src/client.tsx
|
|
950
970
|
const clients = {};
|
|
951
971
|
/**
|
|
952
|
-
*
|
|
972
|
+
* Create and register a FaasReactClient instance.
|
|
973
|
+
*
|
|
974
|
+
* The returned client is stored by `baseUrl` and becomes the default client
|
|
975
|
+
* used by helpers such as {@link faas} and {@link useFaas}.
|
|
953
976
|
*
|
|
954
|
-
* @
|
|
977
|
+
* @param options - Client configuration including base URL, default request options, and error hooks.
|
|
978
|
+
* @returns Registered FaasReactClient instance.
|
|
955
979
|
*
|
|
956
980
|
* @example
|
|
957
981
|
* ```ts
|
|
982
|
+
* import { FaasReactClient } from '@faasjs/react'
|
|
983
|
+
*
|
|
958
984
|
* const client = FaasReactClient({
|
|
959
|
-
* baseUrl: 'localhost:8080/api/'
|
|
985
|
+
* baseUrl: 'http://localhost:8080/api/',
|
|
960
986
|
* })
|
|
961
987
|
* ```
|
|
962
988
|
*/
|
|
@@ -985,16 +1011,20 @@ function FaasReactClient({ baseUrl, options: clientOptions, onError } = { baseUr
|
|
|
985
1011
|
return reactClient;
|
|
986
1012
|
}
|
|
987
1013
|
/**
|
|
988
|
-
* Get FaasReactClient instance
|
|
1014
|
+
* Get a registered FaasReactClient instance.
|
|
1015
|
+
*
|
|
1016
|
+
* When `host` is omitted, the first registered client is returned. If no client
|
|
1017
|
+
* has been created yet, a default client is initialized automatically.
|
|
989
1018
|
*
|
|
990
|
-
* @param host
|
|
991
|
-
* @returns
|
|
1019
|
+
* @param host - Registered base URL to look up. Omit it to use the default client.
|
|
1020
|
+
* @returns Registered or newly created FaasReactClient instance.
|
|
992
1021
|
*
|
|
993
1022
|
* @example
|
|
994
1023
|
* ```ts
|
|
1024
|
+
* import { getClient } from '@faasjs/react'
|
|
1025
|
+
*
|
|
995
1026
|
* getClient()
|
|
996
|
-
*
|
|
997
|
-
* getClient('another-host')
|
|
1027
|
+
* getClient('http://localhost:8080/api/')
|
|
998
1028
|
* ```
|
|
999
1029
|
*/
|
|
1000
1030
|
function getClient(host) {
|
|
@@ -1055,23 +1085,25 @@ var ErrorBoundary = class extends react.Component {
|
|
|
1055
1085
|
* Custom hook that returns a stateful value and a ref to that value.
|
|
1056
1086
|
*
|
|
1057
1087
|
* @template T - The type of the value.
|
|
1058
|
-
* @param
|
|
1059
|
-
* @returns
|
|
1088
|
+
* @param initialValue - Initial state value. When omitted, state starts as `null`.
|
|
1089
|
+
* @returns Tuple containing the current state, the state setter, and a ref that always points at the latest state.
|
|
1060
1090
|
*
|
|
1061
1091
|
* @example
|
|
1062
1092
|
* ```tsx
|
|
1063
1093
|
* import { useStateRef } from '@faasjs/react'
|
|
1064
1094
|
*
|
|
1065
1095
|
* function MyComponent() {
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
*
|
|
1096
|
+
* const [value, setValue, ref] = useStateRef(0)
|
|
1097
|
+
*
|
|
1098
|
+
* return (
|
|
1099
|
+
* <div>
|
|
1100
|
+
* <p>Value: {value}</p>
|
|
1101
|
+
* <button onClick={() => setValue(value + 1)}>Increment</button>
|
|
1102
|
+
* <button onClick={() => console.log(ref.current)}>Submit</button>
|
|
1103
|
+
* </div>
|
|
1104
|
+
* )
|
|
1105
|
+
* }
|
|
1106
|
+
* ```
|
|
1075
1107
|
*/
|
|
1076
1108
|
function useStateRef(initialValue) {
|
|
1077
1109
|
const [state, setState] = (0, react.useState)(initialValue ?? null);
|
|
@@ -1088,15 +1120,16 @@ function useStateRef(initialValue) {
|
|
|
1088
1120
|
//#endregion
|
|
1089
1121
|
//#region src/splittingState.tsx
|
|
1090
1122
|
/**
|
|
1091
|
-
*
|
|
1123
|
+
* Create local state entries and matching setters for each key in an object.
|
|
1092
1124
|
*
|
|
1093
1125
|
* @template T - A generic type that extends a record with string keys and any values.
|
|
1094
|
-
* @param
|
|
1126
|
+
* @param initialStates - Object whose keys become state values and `setXxx` setters.
|
|
1127
|
+
* @returns Object containing the original keys plus generated setter functions.
|
|
1095
1128
|
*
|
|
1096
1129
|
* @example
|
|
1097
1130
|
* ```tsx
|
|
1098
1131
|
* function Counter() {
|
|
1099
|
-
* const { count, setCount, name, setName } = useSplittingState({ count: 0, name: 'John' })
|
|
1132
|
+
* const { count, setCount, name, setName } = useSplittingState({ count: 0, name: 'John' })
|
|
1100
1133
|
*
|
|
1101
1134
|
* return <>{name}: {count}</>
|
|
1102
1135
|
* }
|
|
@@ -1349,34 +1382,30 @@ async function validValues(rules, items, values, lang) {
|
|
|
1349
1382
|
//#region src/Form/Footer.tsx
|
|
1350
1383
|
function FormFooter() {
|
|
1351
1384
|
const { submitting, setSubmitting, onSubmit, valuesRef, Elements, items, setErrors, lang, rules } = useFormContext();
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
setErrors({});
|
|
1355
|
-
const errors = await validValues(rules, items, valuesRef.current, lang);
|
|
1356
|
-
if (Object.keys(errors).length) {
|
|
1357
|
-
setErrors(errors);
|
|
1358
|
-
setSubmitting(false);
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
onSubmit(valuesRef.current).finally(() => setSubmitting(false));
|
|
1362
|
-
}, [
|
|
1363
|
-
setSubmitting,
|
|
1364
|
-
setErrors,
|
|
1365
|
-
rules,
|
|
1366
|
-
items,
|
|
1367
|
-
lang,
|
|
1368
|
-
onSubmit
|
|
1369
|
-
]);
|
|
1370
|
-
return (0, react.useMemo)(() => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Elements.Button, {
|
|
1385
|
+
const Button = Elements.Button;
|
|
1386
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1371
1387
|
submitting,
|
|
1372
|
-
submit:
|
|
1388
|
+
submit: (0, react.useCallback)(async () => {
|
|
1389
|
+
setSubmitting(true);
|
|
1390
|
+
setErrors({});
|
|
1391
|
+
const errors = await validValues(rules, items, valuesRef.current, lang);
|
|
1392
|
+
if (Object.keys(errors).length) {
|
|
1393
|
+
setErrors(errors);
|
|
1394
|
+
setSubmitting(false);
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
onSubmit(valuesRef.current).finally(() => setSubmitting(false));
|
|
1398
|
+
}, [
|
|
1399
|
+
setSubmitting,
|
|
1400
|
+
setErrors,
|
|
1401
|
+
rules,
|
|
1402
|
+
items,
|
|
1403
|
+
valuesRef,
|
|
1404
|
+
lang,
|
|
1405
|
+
onSubmit
|
|
1406
|
+
]),
|
|
1373
1407
|
children: lang.submit
|
|
1374
|
-
})
|
|
1375
|
-
submitting,
|
|
1376
|
-
handleSubmit,
|
|
1377
|
-
lang.submit,
|
|
1378
|
-
Elements.Button
|
|
1379
|
-
]);
|
|
1408
|
+
});
|
|
1380
1409
|
}
|
|
1381
1410
|
FormFooter.displayName = "FormFooter";
|
|
1382
1411
|
//#endregion
|
|
@@ -1395,32 +1424,29 @@ function mergeValues(items, defaultValues = {}) {
|
|
|
1395
1424
|
return values;
|
|
1396
1425
|
}
|
|
1397
1426
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1427
|
+
* Render a form with context, default elements, and validation state.
|
|
1428
|
+
*
|
|
1429
|
+
* `FormContainer` merges provided elements, language strings, and rules with
|
|
1430
|
+
* the package defaults, then exposes them through form context.
|
|
1400
1431
|
*
|
|
1401
1432
|
* @template Values - The type of form values, defaults to Record<string, any>.
|
|
1402
1433
|
* @template FormElements - The type of form elements, defaults to FormElementTypes.
|
|
1403
1434
|
* @template Rules - The type of form rules, defaults to FormDefaultRules.
|
|
1404
|
-
*
|
|
1405
|
-
* @
|
|
1406
|
-
* @param {Values} props.defaultValues - The default values for the form fields.
|
|
1407
|
-
* @param {FormElements} props.Elements - The form elements to be used in the form.
|
|
1408
|
-
* @param {Rules} props.rules - The validation rules for the form fields.
|
|
1409
|
-
* @param {FormLang} props.lang - The language settings for the form.
|
|
1410
|
-
* @param {Partial<FormContextProps>} props - Additional properties for the form context.
|
|
1411
|
-
*
|
|
1412
|
-
* @returns {JSX.Element} The FormContainer component.
|
|
1435
|
+
* @param props - Form items and optional overrides for defaults, language, rules, and submit behavior.
|
|
1436
|
+
* @returns React form container with shared form context.
|
|
1413
1437
|
*
|
|
1414
1438
|
* @example
|
|
1415
1439
|
* ```tsx
|
|
1416
1440
|
* import { Form } from '@faasjs/react'
|
|
1417
1441
|
*
|
|
1418
1442
|
* function MyForm() {
|
|
1419
|
-
* return
|
|
1420
|
-
*
|
|
1421
|
-
* {
|
|
1422
|
-
*
|
|
1423
|
-
*
|
|
1443
|
+
* return (
|
|
1444
|
+
* <Form
|
|
1445
|
+
* items={[
|
|
1446
|
+
* { name: 'name' },
|
|
1447
|
+
* ]}
|
|
1448
|
+
* />
|
|
1449
|
+
* )
|
|
1424
1450
|
* }
|
|
1425
1451
|
* ```
|
|
1426
1452
|
*/
|
|
@@ -1477,14 +1503,21 @@ OptionalWrapper.displayName = "OptionalWrapper";
|
|
|
1477
1503
|
//#endregion
|
|
1478
1504
|
//#region src/useFaasStream.tsx
|
|
1479
1505
|
/**
|
|
1480
|
-
* Stream
|
|
1506
|
+
* Stream a FaasJS response into React state.
|
|
1481
1507
|
*
|
|
1482
|
-
*
|
|
1483
|
-
*
|
|
1484
|
-
*
|
|
1508
|
+
* The hook sends a streaming request, appends decoded text chunks to `data`,
|
|
1509
|
+
* and exposes reload helpers for retrying the same action.
|
|
1510
|
+
*
|
|
1511
|
+
* @param action - Action path to invoke.
|
|
1512
|
+
* @param defaultParams - Params used for the initial request and future reloads.
|
|
1513
|
+
* @param options - Optional hook configuration such as controlled data, debounce, and skip logic.
|
|
1514
|
+
* @returns Streaming request state and helper methods.
|
|
1485
1515
|
*
|
|
1486
1516
|
* @example
|
|
1487
1517
|
* ```tsx
|
|
1518
|
+
* import { useState } from 'react'
|
|
1519
|
+
* import { useFaasStream } from '@faasjs/react'
|
|
1520
|
+
*
|
|
1488
1521
|
* function Chat() {
|
|
1489
1522
|
* const [prompt, setPrompt] = useState('')
|
|
1490
1523
|
* const { data, loading, reload } = useFaasStream('chat', { prompt })
|
|
@@ -1625,8 +1658,8 @@ function useFaasStream(action, defaultParams, options = {}) {
|
|
|
1625
1658
|
* Hook to store the previous value of a state or prop.
|
|
1626
1659
|
*
|
|
1627
1660
|
* @template T - The type of the value.
|
|
1628
|
-
* @param
|
|
1629
|
-
* @returns
|
|
1661
|
+
* @param value - The current value to track.
|
|
1662
|
+
* @returns Previous value from the prior render, or `undefined` on the first render.
|
|
1630
1663
|
*/
|
|
1631
1664
|
function usePrevious(value) {
|
|
1632
1665
|
const ref = (0, react.useRef)(void 0);
|