@faasjs/react 3.0.0 → 3.1.1
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 +10 -0
- package/dist/index.d.mts +60 -7
- package/dist/index.d.ts +60 -7
- package/dist/index.js +110 -49
- package/dist/index.mjs +107 -51
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ React plugin for FaasJS.
|
|
|
15
15
|
- Compatible with [why-did-you-render](https://github.com/welldone-software/why-did-you-render).
|
|
16
16
|
- Additional React functions:
|
|
17
17
|
- Utils:
|
|
18
|
+
- [equal](functions/equal.md): Compare two values for deep equality.
|
|
19
|
+
- [useEqualMemoize](functions/useEqualMemoize.md): Memoize a value with deep equality.
|
|
20
|
+
- [useEqualEffect](functions/useEqualEffect.md): Run an effect with deep equality.
|
|
21
|
+
- [useEqualMemo](functions/useEqualMemo.md): Memoize a value with deep equality.
|
|
22
|
+
- [useEqualCallback](functions/useEqualCallback.md): Memoize a callback with deep equality.
|
|
18
23
|
- [useConstant](functions/useConstant.md): Create a constant value with hooks.
|
|
19
24
|
- [createSplittingContext](functions/createSplittingContext.md): Create a context for code splitting.
|
|
20
25
|
- [OptionalWrapper](functions/OptionalWrapper.md): Render a component optionally.
|
|
@@ -34,12 +39,17 @@ npm install @faasjs/react
|
|
|
34
39
|
## Functions
|
|
35
40
|
|
|
36
41
|
- [createSplittingContext](functions/createSplittingContext.md)
|
|
42
|
+
- [equal](functions/equal.md)
|
|
37
43
|
- [faas](functions/faas.md)
|
|
38
44
|
- [FaasDataWrapper](functions/FaasDataWrapper.md)
|
|
39
45
|
- [FaasReactClient](functions/FaasReactClient.md)
|
|
40
46
|
- [getClient](functions/getClient.md)
|
|
41
47
|
- [OptionalWrapper](functions/OptionalWrapper.md)
|
|
42
48
|
- [useConstant](functions/useConstant.md)
|
|
49
|
+
- [useEqualCallback](functions/useEqualCallback.md)
|
|
50
|
+
- [useEqualEffect](functions/useEqualEffect.md)
|
|
51
|
+
- [useEqualMemo](functions/useEqualMemo.md)
|
|
52
|
+
- [useEqualMemoize](functions/useEqualMemoize.md)
|
|
43
53
|
- [useFaas](functions/useFaas.md)
|
|
44
54
|
- [withFaasData](functions/withFaasData.md)
|
|
45
55
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FaasAction, FaasData, FaasParams } from '@faasjs/types';
|
|
2
2
|
export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
|
|
3
|
-
import { Response,
|
|
3
|
+
import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
|
|
4
4
|
export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
|
|
5
5
|
import * as react from 'react';
|
|
6
6
|
import { ReactNode, ReactElement, Component, ComponentType, ComponentProps } from 'react';
|
|
@@ -14,6 +14,50 @@ declare namespace useConstant {
|
|
|
14
14
|
var whyDidYouRender: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Compares two values for deep equality.
|
|
19
|
+
*
|
|
20
|
+
* This function checks if two values are deeply equal by comparing their types and contents.
|
|
21
|
+
* It handles various data types including primitives, arrays, dates, regular expressions, functions,
|
|
22
|
+
* maps, sets, and promises.
|
|
23
|
+
*
|
|
24
|
+
* @param a - The first value to compare.
|
|
25
|
+
* @param b - The second value to compare.
|
|
26
|
+
* @returns `true` if the values are deeply equal, `false` otherwise.
|
|
27
|
+
*/
|
|
28
|
+
declare function equal(a: any, b: any): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Custom hook that memoizes a value using deep equality comparison.
|
|
31
|
+
*
|
|
32
|
+
* @param value - The value to be memoized.
|
|
33
|
+
* @returns The memoized value.
|
|
34
|
+
*/
|
|
35
|
+
declare function useEqualMemoize(value: any): any;
|
|
36
|
+
/**
|
|
37
|
+
* Custom hook that works like `useEffect` but uses deep comparison on dependencies.
|
|
38
|
+
*
|
|
39
|
+
* @param callback - The effect callback function to run.
|
|
40
|
+
* @param dependencies - The list of dependencies for the effect.
|
|
41
|
+
* @returns The result of the `useEffect` hook with memoized dependencies.
|
|
42
|
+
*/
|
|
43
|
+
declare function useEqualEffect(callback: React.EffectCallback, dependencies: any[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Custom hook that works like `useMemo` but uses deep comparison on dependencies.
|
|
46
|
+
*
|
|
47
|
+
* @param callback - The callback function to run.
|
|
48
|
+
* @param dependencies - The list of dependencies.
|
|
49
|
+
* @returns The result of the `useMemo` hook with memoized dependencies.
|
|
50
|
+
*/
|
|
51
|
+
declare function useEqualMemo<T>(callback: () => T, dependencies: any[]): T;
|
|
52
|
+
/**
|
|
53
|
+
* Custom hook that works like `useCallback` but uses deep comparison on dependencies.
|
|
54
|
+
*
|
|
55
|
+
* @param callback - The callback function to run.
|
|
56
|
+
* @param dependencies - The list of dependencies.
|
|
57
|
+
* @returns The result of the `useCallback` hook with memoized dependencies.
|
|
58
|
+
*/
|
|
59
|
+
declare function useEqualCallback<T extends (...args: any[]) => any>(callback: T, dependencies: any[]): T;
|
|
60
|
+
|
|
17
61
|
/**
|
|
18
62
|
* Creates a splitting context with the given default value.
|
|
19
63
|
*
|
|
@@ -79,6 +123,11 @@ type FaasDataInjection<PathOrData extends FaasAction = any> = {
|
|
|
79
123
|
data: FaasData<PathOrData>;
|
|
80
124
|
error: any;
|
|
81
125
|
promise: Promise<Response<FaasData<PathOrData>>>;
|
|
126
|
+
/**
|
|
127
|
+
* Reloads data with new or existing parameters.
|
|
128
|
+
*
|
|
129
|
+
* **Note**: It will sets skip to false before loading data.
|
|
130
|
+
*/
|
|
82
131
|
reload(params?: Record<string, any>): Promise<Response<PathOrData>>;
|
|
83
132
|
setData: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
84
133
|
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
|
@@ -96,9 +145,9 @@ type FaasDataWrapperProps<PathOrData extends FaasAction> = {
|
|
|
96
145
|
data?: FaasData<PathOrData>;
|
|
97
146
|
/** use custom setData, should work with data */
|
|
98
147
|
setData?: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
99
|
-
baseUrl?:
|
|
148
|
+
baseUrl?: BaseUrl;
|
|
100
149
|
};
|
|
101
|
-
declare function FaasDataWrapper<PathOrData extends FaasAction>(
|
|
150
|
+
declare function FaasDataWrapper<PathOrData extends FaasAction>(props: FaasDataWrapperProps<PathOrData>): JSX.Element;
|
|
102
151
|
declare namespace FaasDataWrapper {
|
|
103
152
|
var whyDidYouRender: boolean;
|
|
104
153
|
}
|
|
@@ -116,11 +165,15 @@ type useFaasOptions<PathOrData extends FaasAction> = {
|
|
|
116
165
|
params?: FaasParams<PathOrData>;
|
|
117
166
|
data?: FaasData<PathOrData>;
|
|
118
167
|
setData?: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
119
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* If skip is true, the request will not be sent.
|
|
170
|
+
*
|
|
171
|
+
* However, you can still use reload to send the request.
|
|
172
|
+
*/
|
|
120
173
|
skip?: boolean | ((params: FaasParams<PathOrData>) => boolean);
|
|
121
|
-
/**
|
|
174
|
+
/** Send the last request after milliseconds */
|
|
122
175
|
debounce?: number;
|
|
123
|
-
baseUrl?:
|
|
176
|
+
baseUrl?: BaseUrl;
|
|
124
177
|
};
|
|
125
178
|
/**
|
|
126
179
|
* Request faas server with React hook
|
|
@@ -256,4 +309,4 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
|
|
|
256
309
|
whyDidYouRender: boolean;
|
|
257
310
|
};
|
|
258
311
|
|
|
259
|
-
export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, faas, getClient, useConstant, useFaas, type useFaasOptions, withFaasData };
|
|
312
|
+
export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, withFaasData };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FaasAction, FaasData, FaasParams } from '@faasjs/types';
|
|
2
2
|
export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
|
|
3
|
-
import { Response,
|
|
3
|
+
import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
|
|
4
4
|
export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
|
|
5
5
|
import * as react from 'react';
|
|
6
6
|
import { ReactNode, ReactElement, Component, ComponentType, ComponentProps } from 'react';
|
|
@@ -14,6 +14,50 @@ declare namespace useConstant {
|
|
|
14
14
|
var whyDidYouRender: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Compares two values for deep equality.
|
|
19
|
+
*
|
|
20
|
+
* This function checks if two values are deeply equal by comparing their types and contents.
|
|
21
|
+
* It handles various data types including primitives, arrays, dates, regular expressions, functions,
|
|
22
|
+
* maps, sets, and promises.
|
|
23
|
+
*
|
|
24
|
+
* @param a - The first value to compare.
|
|
25
|
+
* @param b - The second value to compare.
|
|
26
|
+
* @returns `true` if the values are deeply equal, `false` otherwise.
|
|
27
|
+
*/
|
|
28
|
+
declare function equal(a: any, b: any): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Custom hook that memoizes a value using deep equality comparison.
|
|
31
|
+
*
|
|
32
|
+
* @param value - The value to be memoized.
|
|
33
|
+
* @returns The memoized value.
|
|
34
|
+
*/
|
|
35
|
+
declare function useEqualMemoize(value: any): any;
|
|
36
|
+
/**
|
|
37
|
+
* Custom hook that works like `useEffect` but uses deep comparison on dependencies.
|
|
38
|
+
*
|
|
39
|
+
* @param callback - The effect callback function to run.
|
|
40
|
+
* @param dependencies - The list of dependencies for the effect.
|
|
41
|
+
* @returns The result of the `useEffect` hook with memoized dependencies.
|
|
42
|
+
*/
|
|
43
|
+
declare function useEqualEffect(callback: React.EffectCallback, dependencies: any[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Custom hook that works like `useMemo` but uses deep comparison on dependencies.
|
|
46
|
+
*
|
|
47
|
+
* @param callback - The callback function to run.
|
|
48
|
+
* @param dependencies - The list of dependencies.
|
|
49
|
+
* @returns The result of the `useMemo` hook with memoized dependencies.
|
|
50
|
+
*/
|
|
51
|
+
declare function useEqualMemo<T>(callback: () => T, dependencies: any[]): T;
|
|
52
|
+
/**
|
|
53
|
+
* Custom hook that works like `useCallback` but uses deep comparison on dependencies.
|
|
54
|
+
*
|
|
55
|
+
* @param callback - The callback function to run.
|
|
56
|
+
* @param dependencies - The list of dependencies.
|
|
57
|
+
* @returns The result of the `useCallback` hook with memoized dependencies.
|
|
58
|
+
*/
|
|
59
|
+
declare function useEqualCallback<T extends (...args: any[]) => any>(callback: T, dependencies: any[]): T;
|
|
60
|
+
|
|
17
61
|
/**
|
|
18
62
|
* Creates a splitting context with the given default value.
|
|
19
63
|
*
|
|
@@ -79,6 +123,11 @@ type FaasDataInjection<PathOrData extends FaasAction = any> = {
|
|
|
79
123
|
data: FaasData<PathOrData>;
|
|
80
124
|
error: any;
|
|
81
125
|
promise: Promise<Response<FaasData<PathOrData>>>;
|
|
126
|
+
/**
|
|
127
|
+
* Reloads data with new or existing parameters.
|
|
128
|
+
*
|
|
129
|
+
* **Note**: It will sets skip to false before loading data.
|
|
130
|
+
*/
|
|
82
131
|
reload(params?: Record<string, any>): Promise<Response<PathOrData>>;
|
|
83
132
|
setData: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
84
133
|
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
|
@@ -96,9 +145,9 @@ type FaasDataWrapperProps<PathOrData extends FaasAction> = {
|
|
|
96
145
|
data?: FaasData<PathOrData>;
|
|
97
146
|
/** use custom setData, should work with data */
|
|
98
147
|
setData?: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
99
|
-
baseUrl?:
|
|
148
|
+
baseUrl?: BaseUrl;
|
|
100
149
|
};
|
|
101
|
-
declare function FaasDataWrapper<PathOrData extends FaasAction>(
|
|
150
|
+
declare function FaasDataWrapper<PathOrData extends FaasAction>(props: FaasDataWrapperProps<PathOrData>): JSX.Element;
|
|
102
151
|
declare namespace FaasDataWrapper {
|
|
103
152
|
var whyDidYouRender: boolean;
|
|
104
153
|
}
|
|
@@ -116,11 +165,15 @@ type useFaasOptions<PathOrData extends FaasAction> = {
|
|
|
116
165
|
params?: FaasParams<PathOrData>;
|
|
117
166
|
data?: FaasData<PathOrData>;
|
|
118
167
|
setData?: React.Dispatch<React.SetStateAction<FaasData<PathOrData>>>;
|
|
119
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* If skip is true, the request will not be sent.
|
|
170
|
+
*
|
|
171
|
+
* However, you can still use reload to send the request.
|
|
172
|
+
*/
|
|
120
173
|
skip?: boolean | ((params: FaasParams<PathOrData>) => boolean);
|
|
121
|
-
/**
|
|
174
|
+
/** Send the last request after milliseconds */
|
|
122
175
|
debounce?: number;
|
|
123
|
-
baseUrl?:
|
|
176
|
+
baseUrl?: BaseUrl;
|
|
124
177
|
};
|
|
125
178
|
/**
|
|
126
179
|
* Request faas server with React hook
|
|
@@ -256,4 +309,4 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
|
|
|
256
309
|
whyDidYouRender: boolean;
|
|
257
310
|
};
|
|
258
311
|
|
|
259
|
-
export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, faas, getClient, useConstant, useFaas, type useFaasOptions, withFaasData };
|
|
312
|
+
export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, withFaasData };
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,69 @@ function useConstant(fn) {
|
|
|
13
13
|
return ref.current.v;
|
|
14
14
|
}
|
|
15
15
|
useConstant.whyDidYouRender = true;
|
|
16
|
+
function equal(a, b) {
|
|
17
|
+
if (a === b) return true;
|
|
18
|
+
if ((a === null || a === void 0) && (b === null || b === void 0))
|
|
19
|
+
return true;
|
|
20
|
+
if (typeof a !== typeof b) return false;
|
|
21
|
+
const ctor = a.constructor;
|
|
22
|
+
if (ctor !== b.constructor) return false;
|
|
23
|
+
switch (ctor) {
|
|
24
|
+
case String:
|
|
25
|
+
case Boolean:
|
|
26
|
+
return a === b;
|
|
27
|
+
case Number:
|
|
28
|
+
return Number.isNaN(a) && Number.isNaN(b) || a === b;
|
|
29
|
+
case Array: {
|
|
30
|
+
if (a.length !== b.length) return false;
|
|
31
|
+
for (let i = 0; i < a.length; i++) {
|
|
32
|
+
if (!equal(a[i], b[i])) return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
case Date:
|
|
37
|
+
return a.getTime() === b.getTime();
|
|
38
|
+
case RegExp:
|
|
39
|
+
case Function:
|
|
40
|
+
return a.toString() === b.toString();
|
|
41
|
+
case Map:
|
|
42
|
+
case Set:
|
|
43
|
+
return equal(Array.from(a), Array.from(b));
|
|
44
|
+
case Promise:
|
|
45
|
+
return a === b;
|
|
46
|
+
case Object: {
|
|
47
|
+
const keys = Object.keys(a);
|
|
48
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
if (!equal(a[key], b[key])) return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
default:
|
|
55
|
+
throw Error(`Unsupported type: ${ctor}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function useEqualMemoize(value) {
|
|
59
|
+
const ref = react.useRef(value);
|
|
60
|
+
const signalRef = react.useRef(0);
|
|
61
|
+
if (!equal(value, ref.current)) {
|
|
62
|
+
ref.current = value;
|
|
63
|
+
signalRef.current += 1;
|
|
64
|
+
}
|
|
65
|
+
return react.useMemo(() => ref.current, [signalRef.current]);
|
|
66
|
+
}
|
|
67
|
+
function useEqualEffect(callback, dependencies) {
|
|
68
|
+
return react.useEffect(callback, useEqualMemoize(dependencies));
|
|
69
|
+
}
|
|
70
|
+
function useEqualMemo(callback, dependencies) {
|
|
71
|
+
return react.useMemo(callback, useEqualMemoize(dependencies));
|
|
72
|
+
}
|
|
73
|
+
function useEqualCallback(callback, dependencies) {
|
|
74
|
+
return react.useCallback(
|
|
75
|
+
(...args) => callback(...args),
|
|
76
|
+
useEqualMemoize(dependencies)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
16
79
|
function createSplittingContext(defaultValue) {
|
|
17
80
|
const contexts = {};
|
|
18
81
|
const keys = Object.keys(defaultValue);
|
|
@@ -42,34 +105,28 @@ function createSplittingContext(defaultValue) {
|
|
|
42
105
|
use
|
|
43
106
|
};
|
|
44
107
|
}
|
|
45
|
-
function FaasDataWrapper({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
baseUrl
|
|
55
|
-
}) {
|
|
56
|
-
const request = getClient(baseUrl).useFaas(action, params, {
|
|
57
|
-
data,
|
|
58
|
-
setData
|
|
59
|
-
});
|
|
108
|
+
function FaasDataWrapper(props) {
|
|
109
|
+
const request = getClient(props.baseUrl).useFaas(
|
|
110
|
+
props.action,
|
|
111
|
+
props.params,
|
|
112
|
+
{
|
|
113
|
+
data: props.data,
|
|
114
|
+
setData: props.setData
|
|
115
|
+
}
|
|
116
|
+
);
|
|
60
117
|
const [loaded, setLoaded] = react.useState(false);
|
|
61
118
|
react.useEffect(() => {
|
|
62
119
|
if (!loaded && !request.loading) setLoaded(true);
|
|
63
120
|
}, [request.loading]);
|
|
64
|
-
|
|
65
|
-
if (onDataChange) onDataChange(request);
|
|
66
|
-
}, [
|
|
67
|
-
const child =
|
|
121
|
+
useEqualEffect(() => {
|
|
122
|
+
if (props.onDataChange) props.onDataChange(request);
|
|
123
|
+
}, [request.data]);
|
|
124
|
+
const child = useEqualMemo(() => {
|
|
68
125
|
if (loaded) {
|
|
69
|
-
if (children) return react.cloneElement(children, request);
|
|
70
|
-
if (render) return render(request);
|
|
126
|
+
if (props.children) return react.cloneElement(props.children, request);
|
|
127
|
+
if (props.render) return props.render(request);
|
|
71
128
|
}
|
|
72
|
-
return fallback || null;
|
|
129
|
+
return props.fallback || null;
|
|
73
130
|
}, [
|
|
74
131
|
loaded,
|
|
75
132
|
request.action,
|
|
@@ -84,47 +141,45 @@ FaasDataWrapper.whyDidYouRender = true;
|
|
|
84
141
|
function withFaasData(Component2, faasProps) {
|
|
85
142
|
return (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
|
|
86
143
|
}
|
|
87
|
-
function useFaas(action, defaultParams, options) {
|
|
88
|
-
if (!options) options = {};
|
|
144
|
+
function useFaas(action, defaultParams, options = {}) {
|
|
89
145
|
const [loading, setLoading] = react.useState(true);
|
|
90
146
|
const [data, setData] = react.useState();
|
|
91
147
|
const [error, setError] = react.useState();
|
|
92
|
-
const [promise, setPromise] = react.useState();
|
|
93
148
|
const [params, setParams] = react.useState(defaultParams);
|
|
94
149
|
const [reloadTimes, setReloadTimes] = react.useState(0);
|
|
95
150
|
const [fails, setFails] = react.useState(0);
|
|
96
151
|
const [skip, setSkip] = react.useState(
|
|
97
152
|
typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
|
|
98
153
|
);
|
|
99
|
-
const
|
|
100
|
-
react.
|
|
154
|
+
const promiseRef = react.useRef();
|
|
155
|
+
const controllerRef = react.useRef(null);
|
|
156
|
+
useEqualEffect(() => {
|
|
101
157
|
setSkip(
|
|
102
158
|
typeof options.skip === "function" ? options.skip(params) : options.skip
|
|
103
159
|
);
|
|
104
|
-
}, [
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
react.useEffect(() => {
|
|
108
|
-
if (JSON.stringify(defaultParams) !== JSON.stringify(params)) {
|
|
160
|
+
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
161
|
+
useEqualEffect(() => {
|
|
162
|
+
if (!equal(defaultParams, params)) {
|
|
109
163
|
setParams(defaultParams);
|
|
110
164
|
}
|
|
111
|
-
}, [
|
|
112
|
-
|
|
165
|
+
}, [defaultParams]);
|
|
166
|
+
useEqualEffect(() => {
|
|
113
167
|
if (!action || skip) {
|
|
114
168
|
setLoading(false);
|
|
115
169
|
return;
|
|
116
170
|
}
|
|
117
171
|
setLoading(true);
|
|
118
|
-
|
|
172
|
+
controllerRef.current = new AbortController();
|
|
173
|
+
const client = getClient(options.baseUrl);
|
|
119
174
|
function send() {
|
|
120
175
|
const request = client.faas(
|
|
121
176
|
action,
|
|
122
177
|
options.params || params,
|
|
123
|
-
{ signal:
|
|
178
|
+
{ signal: controllerRef.current.signal }
|
|
124
179
|
);
|
|
125
|
-
|
|
180
|
+
promiseRef.current = request;
|
|
126
181
|
request.then((r) => {
|
|
127
|
-
options
|
|
182
|
+
options.setData ? options.setData(r.data) : setData(r.data);
|
|
128
183
|
setLoading(false);
|
|
129
184
|
}).catch(async (e) => {
|
|
130
185
|
if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
|
|
@@ -145,40 +200,41 @@ function useFaas(action, defaultParams, options) {
|
|
|
145
200
|
return Promise.reject(e);
|
|
146
201
|
});
|
|
147
202
|
}
|
|
148
|
-
if (options
|
|
203
|
+
if (options.debounce) {
|
|
149
204
|
const timeout = setTimeout(send, options.debounce);
|
|
150
205
|
return () => {
|
|
151
206
|
clearTimeout(timeout);
|
|
152
|
-
|
|
207
|
+
controllerRef.current?.abort();
|
|
153
208
|
setLoading(false);
|
|
154
209
|
};
|
|
155
210
|
}
|
|
156
211
|
send();
|
|
157
212
|
return () => {
|
|
158
|
-
|
|
213
|
+
controllerRef.current?.abort();
|
|
159
214
|
setLoading(false);
|
|
160
215
|
};
|
|
161
|
-
}, [action,
|
|
162
|
-
const reload =
|
|
216
|
+
}, [action, options.params || params, reloadTimes, skip]);
|
|
217
|
+
const reload = useEqualCallback(
|
|
163
218
|
(params2) => {
|
|
219
|
+
if (skip) setSkip(false);
|
|
164
220
|
if (params2) setParams(params2);
|
|
165
221
|
setReloadTimes((prev) => prev + 1);
|
|
166
|
-
return
|
|
222
|
+
return promiseRef.current;
|
|
167
223
|
},
|
|
168
|
-
[params]
|
|
224
|
+
[params, skip]
|
|
169
225
|
);
|
|
170
226
|
return {
|
|
171
227
|
action,
|
|
172
228
|
params,
|
|
173
229
|
loading,
|
|
174
|
-
data: options
|
|
230
|
+
data: options.data || data,
|
|
175
231
|
reloadTimes,
|
|
176
232
|
error,
|
|
177
|
-
promise,
|
|
233
|
+
promise: promiseRef.current,
|
|
178
234
|
reload,
|
|
179
|
-
setData: options
|
|
235
|
+
setData: options.setData || setData,
|
|
180
236
|
setLoading,
|
|
181
|
-
setPromise,
|
|
237
|
+
setPromise: (newPromise) => typeof newPromise === "function" ? newPromise(promiseRef.current) : promiseRef.current = newPromise,
|
|
182
238
|
setError
|
|
183
239
|
};
|
|
184
240
|
}
|
|
@@ -265,8 +321,13 @@ exports.FaasDataWrapper = FaasDataWrapper;
|
|
|
265
321
|
exports.FaasReactClient = FaasReactClient;
|
|
266
322
|
exports.OptionalWrapper = OptionalWrapper;
|
|
267
323
|
exports.createSplittingContext = createSplittingContext;
|
|
324
|
+
exports.equal = equal;
|
|
268
325
|
exports.faas = faas;
|
|
269
326
|
exports.getClient = getClient;
|
|
270
327
|
exports.useConstant = useConstant;
|
|
328
|
+
exports.useEqualCallback = useEqualCallback;
|
|
329
|
+
exports.useEqualEffect = useEqualEffect;
|
|
330
|
+
exports.useEqualMemo = useEqualMemo;
|
|
331
|
+
exports.useEqualMemoize = useEqualMemoize;
|
|
271
332
|
exports.useFaas = useFaas;
|
|
272
333
|
exports.withFaasData = withFaasData;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef,
|
|
1
|
+
import { useRef, useMemo, useEffect, useCallback, createContext, useState, cloneElement, Component, useContext } from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { FaasBrowserClient } from '@faasjs/browser';
|
|
4
4
|
|
|
@@ -11,6 +11,69 @@ function useConstant(fn) {
|
|
|
11
11
|
return ref.current.v;
|
|
12
12
|
}
|
|
13
13
|
useConstant.whyDidYouRender = true;
|
|
14
|
+
function equal(a, b) {
|
|
15
|
+
if (a === b) return true;
|
|
16
|
+
if ((a === null || a === void 0) && (b === null || b === void 0))
|
|
17
|
+
return true;
|
|
18
|
+
if (typeof a !== typeof b) return false;
|
|
19
|
+
const ctor = a.constructor;
|
|
20
|
+
if (ctor !== b.constructor) return false;
|
|
21
|
+
switch (ctor) {
|
|
22
|
+
case String:
|
|
23
|
+
case Boolean:
|
|
24
|
+
return a === b;
|
|
25
|
+
case Number:
|
|
26
|
+
return Number.isNaN(a) && Number.isNaN(b) || a === b;
|
|
27
|
+
case Array: {
|
|
28
|
+
if (a.length !== b.length) return false;
|
|
29
|
+
for (let i = 0; i < a.length; i++) {
|
|
30
|
+
if (!equal(a[i], b[i])) return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
case Date:
|
|
35
|
+
return a.getTime() === b.getTime();
|
|
36
|
+
case RegExp:
|
|
37
|
+
case Function:
|
|
38
|
+
return a.toString() === b.toString();
|
|
39
|
+
case Map:
|
|
40
|
+
case Set:
|
|
41
|
+
return equal(Array.from(a), Array.from(b));
|
|
42
|
+
case Promise:
|
|
43
|
+
return a === b;
|
|
44
|
+
case Object: {
|
|
45
|
+
const keys = Object.keys(a);
|
|
46
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
47
|
+
for (const key of keys) {
|
|
48
|
+
if (!equal(a[key], b[key])) return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
throw Error(`Unsupported type: ${ctor}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function useEqualMemoize(value) {
|
|
57
|
+
const ref = useRef(value);
|
|
58
|
+
const signalRef = useRef(0);
|
|
59
|
+
if (!equal(value, ref.current)) {
|
|
60
|
+
ref.current = value;
|
|
61
|
+
signalRef.current += 1;
|
|
62
|
+
}
|
|
63
|
+
return useMemo(() => ref.current, [signalRef.current]);
|
|
64
|
+
}
|
|
65
|
+
function useEqualEffect(callback, dependencies) {
|
|
66
|
+
return useEffect(callback, useEqualMemoize(dependencies));
|
|
67
|
+
}
|
|
68
|
+
function useEqualMemo(callback, dependencies) {
|
|
69
|
+
return useMemo(callback, useEqualMemoize(dependencies));
|
|
70
|
+
}
|
|
71
|
+
function useEqualCallback(callback, dependencies) {
|
|
72
|
+
return useCallback(
|
|
73
|
+
(...args) => callback(...args),
|
|
74
|
+
useEqualMemoize(dependencies)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
14
77
|
function createSplittingContext(defaultValue) {
|
|
15
78
|
const contexts = {};
|
|
16
79
|
const keys = Object.keys(defaultValue);
|
|
@@ -40,34 +103,28 @@ function createSplittingContext(defaultValue) {
|
|
|
40
103
|
use
|
|
41
104
|
};
|
|
42
105
|
}
|
|
43
|
-
function FaasDataWrapper({
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
baseUrl
|
|
53
|
-
}) {
|
|
54
|
-
const request = getClient(baseUrl).useFaas(action, params, {
|
|
55
|
-
data,
|
|
56
|
-
setData
|
|
57
|
-
});
|
|
106
|
+
function FaasDataWrapper(props) {
|
|
107
|
+
const request = getClient(props.baseUrl).useFaas(
|
|
108
|
+
props.action,
|
|
109
|
+
props.params,
|
|
110
|
+
{
|
|
111
|
+
data: props.data,
|
|
112
|
+
setData: props.setData
|
|
113
|
+
}
|
|
114
|
+
);
|
|
58
115
|
const [loaded, setLoaded] = useState(false);
|
|
59
116
|
useEffect(() => {
|
|
60
117
|
if (!loaded && !request.loading) setLoaded(true);
|
|
61
118
|
}, [request.loading]);
|
|
62
|
-
|
|
63
|
-
if (onDataChange) onDataChange(request);
|
|
64
|
-
}, [
|
|
65
|
-
const child =
|
|
119
|
+
useEqualEffect(() => {
|
|
120
|
+
if (props.onDataChange) props.onDataChange(request);
|
|
121
|
+
}, [request.data]);
|
|
122
|
+
const child = useEqualMemo(() => {
|
|
66
123
|
if (loaded) {
|
|
67
|
-
if (children) return cloneElement(children, request);
|
|
68
|
-
if (render) return render(request);
|
|
124
|
+
if (props.children) return cloneElement(props.children, request);
|
|
125
|
+
if (props.render) return props.render(request);
|
|
69
126
|
}
|
|
70
|
-
return fallback || null;
|
|
127
|
+
return props.fallback || null;
|
|
71
128
|
}, [
|
|
72
129
|
loaded,
|
|
73
130
|
request.action,
|
|
@@ -82,47 +139,45 @@ FaasDataWrapper.whyDidYouRender = true;
|
|
|
82
139
|
function withFaasData(Component2, faasProps) {
|
|
83
140
|
return (props) => /* @__PURE__ */ jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsx(Component2, { ...props }) });
|
|
84
141
|
}
|
|
85
|
-
function useFaas(action, defaultParams, options) {
|
|
86
|
-
if (!options) options = {};
|
|
142
|
+
function useFaas(action, defaultParams, options = {}) {
|
|
87
143
|
const [loading, setLoading] = useState(true);
|
|
88
144
|
const [data, setData] = useState();
|
|
89
145
|
const [error, setError] = useState();
|
|
90
|
-
const [promise, setPromise] = useState();
|
|
91
146
|
const [params, setParams] = useState(defaultParams);
|
|
92
147
|
const [reloadTimes, setReloadTimes] = useState(0);
|
|
93
148
|
const [fails, setFails] = useState(0);
|
|
94
149
|
const [skip, setSkip] = useState(
|
|
95
150
|
typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
|
|
96
151
|
);
|
|
97
|
-
const
|
|
98
|
-
|
|
152
|
+
const promiseRef = useRef();
|
|
153
|
+
const controllerRef = useRef(null);
|
|
154
|
+
useEqualEffect(() => {
|
|
99
155
|
setSkip(
|
|
100
156
|
typeof options.skip === "function" ? options.skip(params) : options.skip
|
|
101
157
|
);
|
|
102
|
-
}, [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (JSON.stringify(defaultParams) !== JSON.stringify(params)) {
|
|
158
|
+
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
159
|
+
useEqualEffect(() => {
|
|
160
|
+
if (!equal(defaultParams, params)) {
|
|
107
161
|
setParams(defaultParams);
|
|
108
162
|
}
|
|
109
|
-
}, [
|
|
110
|
-
|
|
163
|
+
}, [defaultParams]);
|
|
164
|
+
useEqualEffect(() => {
|
|
111
165
|
if (!action || skip) {
|
|
112
166
|
setLoading(false);
|
|
113
167
|
return;
|
|
114
168
|
}
|
|
115
169
|
setLoading(true);
|
|
116
|
-
|
|
170
|
+
controllerRef.current = new AbortController();
|
|
171
|
+
const client = getClient(options.baseUrl);
|
|
117
172
|
function send() {
|
|
118
173
|
const request = client.faas(
|
|
119
174
|
action,
|
|
120
175
|
options.params || params,
|
|
121
|
-
{ signal:
|
|
176
|
+
{ signal: controllerRef.current.signal }
|
|
122
177
|
);
|
|
123
|
-
|
|
178
|
+
promiseRef.current = request;
|
|
124
179
|
request.then((r) => {
|
|
125
|
-
options
|
|
180
|
+
options.setData ? options.setData(r.data) : setData(r.data);
|
|
126
181
|
setLoading(false);
|
|
127
182
|
}).catch(async (e) => {
|
|
128
183
|
if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
|
|
@@ -143,40 +198,41 @@ function useFaas(action, defaultParams, options) {
|
|
|
143
198
|
return Promise.reject(e);
|
|
144
199
|
});
|
|
145
200
|
}
|
|
146
|
-
if (options
|
|
201
|
+
if (options.debounce) {
|
|
147
202
|
const timeout = setTimeout(send, options.debounce);
|
|
148
203
|
return () => {
|
|
149
204
|
clearTimeout(timeout);
|
|
150
|
-
|
|
205
|
+
controllerRef.current?.abort();
|
|
151
206
|
setLoading(false);
|
|
152
207
|
};
|
|
153
208
|
}
|
|
154
209
|
send();
|
|
155
210
|
return () => {
|
|
156
|
-
|
|
211
|
+
controllerRef.current?.abort();
|
|
157
212
|
setLoading(false);
|
|
158
213
|
};
|
|
159
|
-
}, [action,
|
|
160
|
-
const reload =
|
|
214
|
+
}, [action, options.params || params, reloadTimes, skip]);
|
|
215
|
+
const reload = useEqualCallback(
|
|
161
216
|
(params2) => {
|
|
217
|
+
if (skip) setSkip(false);
|
|
162
218
|
if (params2) setParams(params2);
|
|
163
219
|
setReloadTimes((prev) => prev + 1);
|
|
164
|
-
return
|
|
220
|
+
return promiseRef.current;
|
|
165
221
|
},
|
|
166
|
-
[params]
|
|
222
|
+
[params, skip]
|
|
167
223
|
);
|
|
168
224
|
return {
|
|
169
225
|
action,
|
|
170
226
|
params,
|
|
171
227
|
loading,
|
|
172
|
-
data: options
|
|
228
|
+
data: options.data || data,
|
|
173
229
|
reloadTimes,
|
|
174
230
|
error,
|
|
175
|
-
promise,
|
|
231
|
+
promise: promiseRef.current,
|
|
176
232
|
reload,
|
|
177
|
-
setData: options
|
|
233
|
+
setData: options.setData || setData,
|
|
178
234
|
setLoading,
|
|
179
|
-
setPromise,
|
|
235
|
+
setPromise: (newPromise) => typeof newPromise === "function" ? newPromise(promiseRef.current) : promiseRef.current = newPromise,
|
|
180
236
|
setError
|
|
181
237
|
};
|
|
182
238
|
}
|
|
@@ -258,4 +314,4 @@ var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
|
|
|
258
314
|
};
|
|
259
315
|
OptionalWrapper.whyDidYouRender = true;
|
|
260
316
|
|
|
261
|
-
export { ErrorBoundary, FaasDataWrapper, FaasReactClient, OptionalWrapper, createSplittingContext, faas, getClient, useConstant, useFaas, withFaasData };
|
|
317
|
+
export { ErrorBoundary, FaasDataWrapper, FaasReactClient, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, withFaasData };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faasjs/react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@faasjs/browser": "3.
|
|
37
|
+
"@faasjs/browser": "3.1.1"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": "*"
|