@faasjs/react 3.0.0-canary.4 → 3.1.0
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 +112 -49
- package/dist/index.mjs +109 -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,71 @@ 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
|
+
console.log(value, ref.current);
|
|
62
|
+
if (!equal(value, ref.current)) {
|
|
63
|
+
ref.current = value;
|
|
64
|
+
signalRef.current += 1;
|
|
65
|
+
console.log("signalRef.current", signalRef.current);
|
|
66
|
+
}
|
|
67
|
+
return react.useMemo(() => ref.current, [signalRef.current]);
|
|
68
|
+
}
|
|
69
|
+
function useEqualEffect(callback, dependencies) {
|
|
70
|
+
return react.useEffect(callback, useEqualMemoize(dependencies));
|
|
71
|
+
}
|
|
72
|
+
function useEqualMemo(callback, dependencies) {
|
|
73
|
+
return react.useMemo(callback, useEqualMemoize(dependencies));
|
|
74
|
+
}
|
|
75
|
+
function useEqualCallback(callback, dependencies) {
|
|
76
|
+
return react.useCallback(
|
|
77
|
+
(...args) => callback(...args),
|
|
78
|
+
useEqualMemoize(dependencies)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
16
81
|
function createSplittingContext(defaultValue) {
|
|
17
82
|
const contexts = {};
|
|
18
83
|
const keys = Object.keys(defaultValue);
|
|
@@ -42,34 +107,28 @@ function createSplittingContext(defaultValue) {
|
|
|
42
107
|
use
|
|
43
108
|
};
|
|
44
109
|
}
|
|
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
|
-
});
|
|
110
|
+
function FaasDataWrapper(props) {
|
|
111
|
+
const request = getClient(props.baseUrl).useFaas(
|
|
112
|
+
props.action,
|
|
113
|
+
props.params,
|
|
114
|
+
{
|
|
115
|
+
data: props.data,
|
|
116
|
+
setData: props.setData
|
|
117
|
+
}
|
|
118
|
+
);
|
|
60
119
|
const [loaded, setLoaded] = react.useState(false);
|
|
61
120
|
react.useEffect(() => {
|
|
62
121
|
if (!loaded && !request.loading) setLoaded(true);
|
|
63
122
|
}, [request.loading]);
|
|
64
|
-
|
|
65
|
-
if (onDataChange) onDataChange(request);
|
|
66
|
-
}, [
|
|
67
|
-
const child =
|
|
123
|
+
useEqualEffect(() => {
|
|
124
|
+
if (props.onDataChange) props.onDataChange(request);
|
|
125
|
+
}, [request.data]);
|
|
126
|
+
const child = useEqualMemo(() => {
|
|
68
127
|
if (loaded) {
|
|
69
|
-
if (children) return react.cloneElement(children, request);
|
|
70
|
-
if (render) return render(request);
|
|
128
|
+
if (props.children) return react.cloneElement(props.children, request);
|
|
129
|
+
if (props.render) return props.render(request);
|
|
71
130
|
}
|
|
72
|
-
return fallback || null;
|
|
131
|
+
return props.fallback || null;
|
|
73
132
|
}, [
|
|
74
133
|
loaded,
|
|
75
134
|
request.action,
|
|
@@ -84,47 +143,45 @@ FaasDataWrapper.whyDidYouRender = true;
|
|
|
84
143
|
function withFaasData(Component2, faasProps) {
|
|
85
144
|
return (props) => /* @__PURE__ */ jsxRuntime.jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsxRuntime.jsx(Component2, { ...props }) });
|
|
86
145
|
}
|
|
87
|
-
function useFaas(action, defaultParams, options) {
|
|
88
|
-
if (!options) options = {};
|
|
146
|
+
function useFaas(action, defaultParams, options = {}) {
|
|
89
147
|
const [loading, setLoading] = react.useState(true);
|
|
90
148
|
const [data, setData] = react.useState();
|
|
91
149
|
const [error, setError] = react.useState();
|
|
92
|
-
const [promise, setPromise] = react.useState();
|
|
93
150
|
const [params, setParams] = react.useState(defaultParams);
|
|
94
151
|
const [reloadTimes, setReloadTimes] = react.useState(0);
|
|
95
152
|
const [fails, setFails] = react.useState(0);
|
|
96
153
|
const [skip, setSkip] = react.useState(
|
|
97
154
|
typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
|
|
98
155
|
);
|
|
99
|
-
const
|
|
100
|
-
react.
|
|
156
|
+
const promiseRef = react.useRef();
|
|
157
|
+
const controllerRef = react.useRef(null);
|
|
158
|
+
useEqualEffect(() => {
|
|
101
159
|
setSkip(
|
|
102
160
|
typeof options.skip === "function" ? options.skip(params) : options.skip
|
|
103
161
|
);
|
|
104
|
-
}, [
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
react.useEffect(() => {
|
|
108
|
-
if (JSON.stringify(defaultParams) !== JSON.stringify(params)) {
|
|
162
|
+
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
163
|
+
useEqualEffect(() => {
|
|
164
|
+
if (!equal(defaultParams, params)) {
|
|
109
165
|
setParams(defaultParams);
|
|
110
166
|
}
|
|
111
|
-
}, [
|
|
112
|
-
|
|
167
|
+
}, [defaultParams]);
|
|
168
|
+
useEqualEffect(() => {
|
|
113
169
|
if (!action || skip) {
|
|
114
170
|
setLoading(false);
|
|
115
171
|
return;
|
|
116
172
|
}
|
|
117
173
|
setLoading(true);
|
|
118
|
-
|
|
174
|
+
controllerRef.current = new AbortController();
|
|
175
|
+
const client = getClient(options.baseUrl);
|
|
119
176
|
function send() {
|
|
120
177
|
const request = client.faas(
|
|
121
178
|
action,
|
|
122
179
|
options.params || params,
|
|
123
|
-
{ signal:
|
|
180
|
+
{ signal: controllerRef.current.signal }
|
|
124
181
|
);
|
|
125
|
-
|
|
182
|
+
promiseRef.current = request;
|
|
126
183
|
request.then((r) => {
|
|
127
|
-
options
|
|
184
|
+
options.setData ? options.setData(r.data) : setData(r.data);
|
|
128
185
|
setLoading(false);
|
|
129
186
|
}).catch(async (e) => {
|
|
130
187
|
if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
|
|
@@ -145,40 +202,41 @@ function useFaas(action, defaultParams, options) {
|
|
|
145
202
|
return Promise.reject(e);
|
|
146
203
|
});
|
|
147
204
|
}
|
|
148
|
-
if (options
|
|
205
|
+
if (options.debounce) {
|
|
149
206
|
const timeout = setTimeout(send, options.debounce);
|
|
150
207
|
return () => {
|
|
151
208
|
clearTimeout(timeout);
|
|
152
|
-
|
|
209
|
+
controllerRef.current?.abort();
|
|
153
210
|
setLoading(false);
|
|
154
211
|
};
|
|
155
212
|
}
|
|
156
213
|
send();
|
|
157
214
|
return () => {
|
|
158
|
-
|
|
215
|
+
controllerRef.current?.abort();
|
|
159
216
|
setLoading(false);
|
|
160
217
|
};
|
|
161
|
-
}, [action,
|
|
162
|
-
const reload =
|
|
218
|
+
}, [action, options.params || params, reloadTimes, skip]);
|
|
219
|
+
const reload = useEqualCallback(
|
|
163
220
|
(params2) => {
|
|
221
|
+
if (skip) setSkip(false);
|
|
164
222
|
if (params2) setParams(params2);
|
|
165
223
|
setReloadTimes((prev) => prev + 1);
|
|
166
|
-
return
|
|
224
|
+
return promiseRef.current;
|
|
167
225
|
},
|
|
168
|
-
[params]
|
|
226
|
+
[params, skip]
|
|
169
227
|
);
|
|
170
228
|
return {
|
|
171
229
|
action,
|
|
172
230
|
params,
|
|
173
231
|
loading,
|
|
174
|
-
data: options
|
|
232
|
+
data: options.data || data,
|
|
175
233
|
reloadTimes,
|
|
176
234
|
error,
|
|
177
|
-
promise,
|
|
235
|
+
promise: promiseRef.current,
|
|
178
236
|
reload,
|
|
179
|
-
setData: options
|
|
237
|
+
setData: options.setData || setData,
|
|
180
238
|
setLoading,
|
|
181
|
-
setPromise,
|
|
239
|
+
setPromise: (newPromise) => typeof newPromise === "function" ? newPromise(promiseRef.current) : promiseRef.current = newPromise,
|
|
182
240
|
setError
|
|
183
241
|
};
|
|
184
242
|
}
|
|
@@ -265,8 +323,13 @@ exports.FaasDataWrapper = FaasDataWrapper;
|
|
|
265
323
|
exports.FaasReactClient = FaasReactClient;
|
|
266
324
|
exports.OptionalWrapper = OptionalWrapper;
|
|
267
325
|
exports.createSplittingContext = createSplittingContext;
|
|
326
|
+
exports.equal = equal;
|
|
268
327
|
exports.faas = faas;
|
|
269
328
|
exports.getClient = getClient;
|
|
270
329
|
exports.useConstant = useConstant;
|
|
330
|
+
exports.useEqualCallback = useEqualCallback;
|
|
331
|
+
exports.useEqualEffect = useEqualEffect;
|
|
332
|
+
exports.useEqualMemo = useEqualMemo;
|
|
333
|
+
exports.useEqualMemoize = useEqualMemoize;
|
|
271
334
|
exports.useFaas = useFaas;
|
|
272
335
|
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,71 @@ 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
|
+
console.log(value, ref.current);
|
|
60
|
+
if (!equal(value, ref.current)) {
|
|
61
|
+
ref.current = value;
|
|
62
|
+
signalRef.current += 1;
|
|
63
|
+
console.log("signalRef.current", signalRef.current);
|
|
64
|
+
}
|
|
65
|
+
return useMemo(() => ref.current, [signalRef.current]);
|
|
66
|
+
}
|
|
67
|
+
function useEqualEffect(callback, dependencies) {
|
|
68
|
+
return useEffect(callback, useEqualMemoize(dependencies));
|
|
69
|
+
}
|
|
70
|
+
function useEqualMemo(callback, dependencies) {
|
|
71
|
+
return useMemo(callback, useEqualMemoize(dependencies));
|
|
72
|
+
}
|
|
73
|
+
function useEqualCallback(callback, dependencies) {
|
|
74
|
+
return useCallback(
|
|
75
|
+
(...args) => callback(...args),
|
|
76
|
+
useEqualMemoize(dependencies)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
14
79
|
function createSplittingContext(defaultValue) {
|
|
15
80
|
const contexts = {};
|
|
16
81
|
const keys = Object.keys(defaultValue);
|
|
@@ -40,34 +105,28 @@ function createSplittingContext(defaultValue) {
|
|
|
40
105
|
use
|
|
41
106
|
};
|
|
42
107
|
}
|
|
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
|
-
});
|
|
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
|
+
);
|
|
58
117
|
const [loaded, setLoaded] = useState(false);
|
|
59
118
|
useEffect(() => {
|
|
60
119
|
if (!loaded && !request.loading) setLoaded(true);
|
|
61
120
|
}, [request.loading]);
|
|
62
|
-
|
|
63
|
-
if (onDataChange) onDataChange(request);
|
|
64
|
-
}, [
|
|
65
|
-
const child =
|
|
121
|
+
useEqualEffect(() => {
|
|
122
|
+
if (props.onDataChange) props.onDataChange(request);
|
|
123
|
+
}, [request.data]);
|
|
124
|
+
const child = useEqualMemo(() => {
|
|
66
125
|
if (loaded) {
|
|
67
|
-
if (children) return cloneElement(children, request);
|
|
68
|
-
if (render) return render(request);
|
|
126
|
+
if (props.children) return cloneElement(props.children, request);
|
|
127
|
+
if (props.render) return props.render(request);
|
|
69
128
|
}
|
|
70
|
-
return fallback || null;
|
|
129
|
+
return props.fallback || null;
|
|
71
130
|
}, [
|
|
72
131
|
loaded,
|
|
73
132
|
request.action,
|
|
@@ -82,47 +141,45 @@ FaasDataWrapper.whyDidYouRender = true;
|
|
|
82
141
|
function withFaasData(Component2, faasProps) {
|
|
83
142
|
return (props) => /* @__PURE__ */ jsx(FaasDataWrapper, { ...faasProps, children: /* @__PURE__ */ jsx(Component2, { ...props }) });
|
|
84
143
|
}
|
|
85
|
-
function useFaas(action, defaultParams, options) {
|
|
86
|
-
if (!options) options = {};
|
|
144
|
+
function useFaas(action, defaultParams, options = {}) {
|
|
87
145
|
const [loading, setLoading] = useState(true);
|
|
88
146
|
const [data, setData] = useState();
|
|
89
147
|
const [error, setError] = useState();
|
|
90
|
-
const [promise, setPromise] = useState();
|
|
91
148
|
const [params, setParams] = useState(defaultParams);
|
|
92
149
|
const [reloadTimes, setReloadTimes] = useState(0);
|
|
93
150
|
const [fails, setFails] = useState(0);
|
|
94
151
|
const [skip, setSkip] = useState(
|
|
95
152
|
typeof options.skip === "function" ? options.skip(defaultParams) : options.skip
|
|
96
153
|
);
|
|
97
|
-
const
|
|
98
|
-
|
|
154
|
+
const promiseRef = useRef();
|
|
155
|
+
const controllerRef = useRef(null);
|
|
156
|
+
useEqualEffect(() => {
|
|
99
157
|
setSkip(
|
|
100
158
|
typeof options.skip === "function" ? options.skip(params) : options.skip
|
|
101
159
|
);
|
|
102
|
-
}, [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
if (JSON.stringify(defaultParams) !== JSON.stringify(params)) {
|
|
160
|
+
}, [typeof options.skip === "function" ? params : options.skip]);
|
|
161
|
+
useEqualEffect(() => {
|
|
162
|
+
if (!equal(defaultParams, params)) {
|
|
107
163
|
setParams(defaultParams);
|
|
108
164
|
}
|
|
109
|
-
}, [
|
|
110
|
-
|
|
165
|
+
}, [defaultParams]);
|
|
166
|
+
useEqualEffect(() => {
|
|
111
167
|
if (!action || skip) {
|
|
112
168
|
setLoading(false);
|
|
113
169
|
return;
|
|
114
170
|
}
|
|
115
171
|
setLoading(true);
|
|
116
|
-
|
|
172
|
+
controllerRef.current = new AbortController();
|
|
173
|
+
const client = getClient(options.baseUrl);
|
|
117
174
|
function send() {
|
|
118
175
|
const request = client.faas(
|
|
119
176
|
action,
|
|
120
177
|
options.params || params,
|
|
121
|
-
{ signal:
|
|
178
|
+
{ signal: controllerRef.current.signal }
|
|
122
179
|
);
|
|
123
|
-
|
|
180
|
+
promiseRef.current = request;
|
|
124
181
|
request.then((r) => {
|
|
125
|
-
options
|
|
182
|
+
options.setData ? options.setData(r.data) : setData(r.data);
|
|
126
183
|
setLoading(false);
|
|
127
184
|
}).catch(async (e) => {
|
|
128
185
|
if (typeof e?.message === "string" && e.message.toLowerCase().indexOf("aborted") >= 0)
|
|
@@ -143,40 +200,41 @@ function useFaas(action, defaultParams, options) {
|
|
|
143
200
|
return Promise.reject(e);
|
|
144
201
|
});
|
|
145
202
|
}
|
|
146
|
-
if (options
|
|
203
|
+
if (options.debounce) {
|
|
147
204
|
const timeout = setTimeout(send, options.debounce);
|
|
148
205
|
return () => {
|
|
149
206
|
clearTimeout(timeout);
|
|
150
|
-
|
|
207
|
+
controllerRef.current?.abort();
|
|
151
208
|
setLoading(false);
|
|
152
209
|
};
|
|
153
210
|
}
|
|
154
211
|
send();
|
|
155
212
|
return () => {
|
|
156
|
-
|
|
213
|
+
controllerRef.current?.abort();
|
|
157
214
|
setLoading(false);
|
|
158
215
|
};
|
|
159
|
-
}, [action,
|
|
160
|
-
const reload =
|
|
216
|
+
}, [action, options.params || params, reloadTimes, skip]);
|
|
217
|
+
const reload = useEqualCallback(
|
|
161
218
|
(params2) => {
|
|
219
|
+
if (skip) setSkip(false);
|
|
162
220
|
if (params2) setParams(params2);
|
|
163
221
|
setReloadTimes((prev) => prev + 1);
|
|
164
|
-
return
|
|
222
|
+
return promiseRef.current;
|
|
165
223
|
},
|
|
166
|
-
[params]
|
|
224
|
+
[params, skip]
|
|
167
225
|
);
|
|
168
226
|
return {
|
|
169
227
|
action,
|
|
170
228
|
params,
|
|
171
229
|
loading,
|
|
172
|
-
data: options
|
|
230
|
+
data: options.data || data,
|
|
173
231
|
reloadTimes,
|
|
174
232
|
error,
|
|
175
|
-
promise,
|
|
233
|
+
promise: promiseRef.current,
|
|
176
234
|
reload,
|
|
177
|
-
setData: options
|
|
235
|
+
setData: options.setData || setData,
|
|
178
236
|
setLoading,
|
|
179
|
-
setPromise,
|
|
237
|
+
setPromise: (newPromise) => typeof newPromise === "function" ? newPromise(promiseRef.current) : promiseRef.current = newPromise,
|
|
180
238
|
setError
|
|
181
239
|
};
|
|
182
240
|
}
|
|
@@ -258,4 +316,4 @@ var OptionalWrapper = ({ condition, Wrapper, wrapperProps, children }) => {
|
|
|
258
316
|
};
|
|
259
317
|
OptionalWrapper.whyDidYouRender = true;
|
|
260
318
|
|
|
261
|
-
export { ErrorBoundary, FaasDataWrapper, FaasReactClient, OptionalWrapper, createSplittingContext, faas, getClient, useConstant, useFaas, withFaasData };
|
|
319
|
+
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.0",
|
|
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.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": "*"
|