@dynamic-labs/react-hooks 4.0.0-alpha.4 → 4.0.0-alpha.41
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/index.cjs +94 -0
- package/index.js +4 -1543
- package/package.json +6 -5
package/index.cjs
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var assertPackageVersion = require('@dynamic-labs/assert-package-version');
|
|
6
|
+
var react = require('react');
|
|
7
|
+
var reactivity = require('@vue/reactivity');
|
|
8
|
+
|
|
9
|
+
var version = "4.0.0-alpha.41";
|
|
10
|
+
|
|
11
|
+
const isObject = value => typeof value === 'object' && value !== null && value !== undefined && typeof value !== 'function' && typeof value !== 'symbol' && typeof value !== 'bigint' && !Array.isArray(value) && !(value instanceof Date);
|
|
12
|
+
/**
|
|
13
|
+
* Watches for changes in the source and calls the callback when the source changes
|
|
14
|
+
* @param source - The source to watch for changes
|
|
15
|
+
* @param callback - The callback to call when the source changes
|
|
16
|
+
* @returns The unsubscribe function
|
|
17
|
+
*/
|
|
18
|
+
const watch = (source, callback) => {
|
|
19
|
+
let isWatching = false;
|
|
20
|
+
const runner = reactivity.effect(source, {
|
|
21
|
+
onTrack: () => isWatching = true,
|
|
22
|
+
scheduler: callback
|
|
23
|
+
});
|
|
24
|
+
if (!isWatching) return undefined;
|
|
25
|
+
return () => {
|
|
26
|
+
reactivity.stop(runner);
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const useReactivityProxy = target => {
|
|
30
|
+
/**
|
|
31
|
+
* Stores all unsubscribe functions in a particular render
|
|
32
|
+
*/
|
|
33
|
+
const unsubscribeFunctionsRef = react.useRef(new Set());
|
|
34
|
+
/**
|
|
35
|
+
* Store all new proxy objects references created when accessed to present unnecessary reactivity.
|
|
36
|
+
* When an attribute is accessed by the customer and we create a proxy for it, we store it here
|
|
37
|
+
* so in the next render, we can reuse the same proxy object and that way not cause a new render
|
|
38
|
+
* by creating a new proxy object reference
|
|
39
|
+
*/
|
|
40
|
+
const proxyObjectCacheRef = react.useRef(new Map());
|
|
41
|
+
// Rerender count to force rerender when the target changes
|
|
42
|
+
const [, setRerenderCount] = react.useState(0);
|
|
43
|
+
const unsubscribeAll = react.useCallback(() => {
|
|
44
|
+
unsubscribeFunctionsRef.current.forEach(unsubscribeFunction => unsubscribeFunction());
|
|
45
|
+
unsubscribeFunctionsRef.current.clear();
|
|
46
|
+
}, []);
|
|
47
|
+
const rerender = react.useCallback(() => {
|
|
48
|
+
unsubscribeAll();
|
|
49
|
+
setRerenderCount(rerenderCount => rerenderCount + 1);
|
|
50
|
+
}, [setRerenderCount, unsubscribeAll]);
|
|
51
|
+
/**
|
|
52
|
+
* On every render it cleans the previous subscriptions.
|
|
53
|
+
* This is necessary because different renders can have different subscriptions.
|
|
54
|
+
*/
|
|
55
|
+
unsubscribeAll();
|
|
56
|
+
const addSubscriptionProxy = (target, proxyCacheKey) => {
|
|
57
|
+
// Check if object path is already cached
|
|
58
|
+
if (proxyObjectCacheRef.current.has(proxyCacheKey)) {
|
|
59
|
+
return proxyObjectCacheRef.current.get(proxyCacheKey);
|
|
60
|
+
}
|
|
61
|
+
// Create a proxy object to subscribe to the accessed attribute
|
|
62
|
+
const proxyObject = new Proxy(target, {
|
|
63
|
+
get: (target, prop, receiver) => {
|
|
64
|
+
const value = Reflect.get(target, prop, receiver);
|
|
65
|
+
// Subscribe to the accessed attribute
|
|
66
|
+
const unsubscribeFunction = watch(() => Reflect.get(target, prop, receiver), rerender);
|
|
67
|
+
if (unsubscribeFunction) {
|
|
68
|
+
unsubscribeFunctionsRef.current.add(unsubscribeFunction);
|
|
69
|
+
}
|
|
70
|
+
const isReactiveValue = Boolean(unsubscribeFunction);
|
|
71
|
+
// If the value is an object, we need to create a proxy for it too
|
|
72
|
+
// but not when the value is reactive, if it is reactive we wanna stop creating proxies
|
|
73
|
+
if (isObject(value) && !isReactiveValue) {
|
|
74
|
+
const propCachePath = `${proxyCacheKey}.${String(prop)}`;
|
|
75
|
+
return addSubscriptionProxy(value, propCachePath);
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Cache the proxy object
|
|
81
|
+
proxyObjectCacheRef.current.set(proxyCacheKey, proxyObject);
|
|
82
|
+
return proxyObject;
|
|
83
|
+
};
|
|
84
|
+
return react.useMemo(() => {
|
|
85
|
+
// target changed, clear all proxy cache to allow for the new object to be proxied
|
|
86
|
+
proxyObjectCacheRef.current.clear();
|
|
87
|
+
return addSubscriptionProxy(target, '');
|
|
88
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
89
|
+
}, [target]);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
assertPackageVersion.assertPackageVersion('@dynamic-labs/react-hooks', version);
|
|
93
|
+
|
|
94
|
+
exports.useReactiveClient = useReactivityProxy;
|