react-state-custom 1.0.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.
@@ -0,0 +1,115 @@
1
+ /**
2
+ * useQuickSubscribe is a custom React hook for efficiently subscribing to specific properties of a context's data object.
3
+ *
4
+ * @template D - The shape of the context data.
5
+ * @param {Context<D> | undefined} ctx - The context object containing data and a subscribe method.
6
+ * @returns {Partial<D>} A proxy object that mirrors the context data, automatically subscribing to properties as they are accessed.
7
+ *
8
+ * This hook tracks which properties of the context data are accessed by the component and subscribes to updates for only those properties.
9
+ * When any of the subscribed properties change, the hook triggers a re-render. Subscriptions are managed and cleaned up automatically
10
+ * when the component unmounts or the context changes. This approach minimizes unnecessary re-renders and resource usage by only
11
+ * subscribing to the data that the component actually uses.
12
+ *
13
+ * Example usage:
14
+ * const {name} = useQuickSubscribe(userContext);
15
+ * // Accessing name will subscribe to changes in 'name' only
16
+ * return <div>{name}</div>;
17
+ */
18
+
19
+ import { debounce } from "lodash-es";
20
+ import { useState, useMemo, useEffect } from "react";
21
+ import type { Context } from "./ctx";
22
+
23
+
24
+ export const useQuickSubscribe = <D>(
25
+ ctx: Context<D> | undefined
26
+ ): {
27
+ [P in keyof D]?: D[P] | undefined;
28
+ } => {
29
+
30
+ const [, setCounter] = useState(0);
31
+
32
+ const { proxy, clean, handleOnChange } = useMemo(
33
+ () => {
34
+
35
+ const allKeys = new Set<keyof D>()
36
+ const allUnsubInstance = new Map<keyof D, Function>()
37
+ const allCompareValue = ({} as Partial<D>)
38
+
39
+ const handleOnChange = debounce(() => {
40
+ console.log("handleOnChange",allCompareValue)
41
+ if (ctx && Object
42
+ .keys(allCompareValue)
43
+ .some((i: any) => allCompareValue[i as keyof D] != ctx?.data[i as keyof D])) {
44
+ setCounter(c => c + 1);
45
+ }
46
+ }, 1)
47
+
48
+ const handleChangeKey = debounce(() => {
49
+ if (ctx) {
50
+ console.log("handleChangeKey")
51
+ let shouldUpdate = false;
52
+ let keyToDelete: (keyof D)[] = []
53
+
54
+ for (let [k, unsub] of allUnsubInstance) {
55
+ if (!allKeys.has(k)) {
56
+ console.log("Remove", k)
57
+ unsub?.();
58
+ keyToDelete.push(k)
59
+ }
60
+ }
61
+ keyToDelete.forEach(k => {
62
+ allUnsubInstance.delete(k);
63
+ delete allCompareValue[k];
64
+ })
65
+ for (let k of allKeys) {
66
+ if (!allUnsubInstance.has(k)) {
67
+ console.log("Add ", k)
68
+ const sub = ctx.subscribe(k, handleOnChange);
69
+ allUnsubInstance.set(k, sub);
70
+ shouldUpdate = true;
71
+ }
72
+ }
73
+ allKeys.clear()
74
+ if (shouldUpdate) handleOnChange?.();
75
+ }
76
+ }, 0)
77
+
78
+ const handleAddKey = (p: keyof D) => {
79
+ allKeys.add(p);
80
+ handleChangeKey();
81
+ }
82
+
83
+ const proxy = new Proxy(
84
+ ctx?.data as any,
85
+ {
86
+ get(target, p) {
87
+ handleAddKey(p as keyof D);
88
+ console.log({ [p]: target[p] });
89
+ return allCompareValue[p as keyof D] = target[p];
90
+ }
91
+ }
92
+ ) as any
93
+
94
+ const clean = () => {
95
+ console.log("Clean", allKeys)
96
+ handleChangeKey?.()
97
+ }
98
+
99
+ console.log("NEW")
100
+ return { proxy, clean, handleOnChange }
101
+ },
102
+ [ctx]
103
+ )
104
+
105
+ useEffect(() => () => clean?.(), [clean])
106
+
107
+ // useEffect(() => {
108
+ // let i = setInterval(handleOnChange, 5000);
109
+ // return () => clearInterval(i);
110
+ // },[handleOnChange])
111
+
112
+ return proxy;
113
+
114
+
115
+ };
@@ -0,0 +1,93 @@
1
+
2
+ import { debounce } from "lodash-es";
3
+ import { useState, useMemo, useEffect } from "react";
4
+ import type { Context } from "./ctx";
5
+
6
+
7
+ export const useQuickSubscribeV2 = <D>(
8
+ ctx: Context<D> | undefined
9
+ ): {
10
+ [P in keyof D]?: D[P] | undefined;
11
+ } => {
12
+
13
+ const [, setCounter] = useState(0);
14
+
15
+ const { proxy, finalGetter, openGetter, clean } = useMemo(
16
+ () => {
17
+
18
+ const allKeys = new Set<keyof D>()
19
+ const allCompareValue: { [P in keyof D]?: D[P] | undefined; } = {}
20
+ const allUnsub = new Map()
21
+
22
+ const proxy = new Proxy(
23
+ ctx?.data as any,
24
+ {
25
+ get(target, p) {
26
+ if (isOpenGetter) {
27
+ allKeys.add(p as keyof D)
28
+ return allCompareValue[p as keyof D] = target[p];
29
+ } else {
30
+ throw new Error("now allow here")
31
+ }
32
+ }
33
+ }
34
+ ) as any
35
+
36
+ let isOpenGetter = true;
37
+
38
+
39
+ let onChange = debounce(() => {
40
+ if ([...allKeys.values()]
41
+ .some(k => allCompareValue[k] != ctx?.data?.[k])) {
42
+ setCounter(c => c + 1)
43
+ }
44
+ }, 0)
45
+
46
+ let openGetter = () => {
47
+ isOpenGetter = true
48
+ allKeys.clear()
49
+ }
50
+
51
+ let finalGetter = () => {
52
+ isOpenGetter = false;
53
+
54
+ [...allKeys.values()]
55
+ .filter(k => !allUnsub.has(k))
56
+ .forEach(k => {
57
+ allUnsub.set(k, ctx?.subscribe(k, onChange))
58
+ });
59
+
60
+ [...allUnsub.keys()]
61
+ .filter(k => !allKeys.has(k))
62
+ .forEach(k => {
63
+ let unsub = allUnsub.get(k)
64
+ unsub?.();
65
+ allUnsub.delete(k);
66
+ });
67
+
68
+ }
69
+
70
+ let clean = () => {
71
+ openGetter();
72
+ finalGetter();
73
+ setCounter(c => c + 1)
74
+ }
75
+
76
+ return { proxy, finalGetter, openGetter, clean }
77
+ },
78
+ []
79
+ )
80
+
81
+ openGetter();
82
+
83
+ setTimeout(finalGetter, 0)
84
+
85
+ useEffect(
86
+ () => () => clean(),
87
+ [clean]
88
+ )
89
+
90
+ return proxy;
91
+
92
+
93
+ };
@@ -0,0 +1,8 @@
1
+ import { useMemo } from "react"
2
+
3
+
4
+ export const useRefValue = <T extends object>(e: T) => {
5
+ const ref = useMemo(() => ({ ...e }), [])
6
+ Object.assign(ref, e)
7
+ return ref
8
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "lib": ["dom", "esnext"],
6
+ "moduleResolution": "node",
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "outDir": "./dist",
13
+ "declaration": true,
14
+ "sourceMap": true
15
+ },
16
+ "include": ["src"],
17
+ "exclude": ["node_modules", "dist"]
18
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,33 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import dts from 'vite-plugin-dts'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ react(),
8
+ dts({
9
+ include: ['src'],
10
+ })
11
+ ],
12
+ build: {
13
+ lib: {
14
+ entry: 'src/index.ts',
15
+ name: 'RState',
16
+ fileName: (format) => `index.${format}.js`,
17
+ formats: ['es', 'umd'],
18
+ },
19
+ rollupOptions: {
20
+ // Ensure to externalize deps that shouldn't be bundled
21
+ external: ['react', 'react-dom'],
22
+ output: {
23
+ globals: {
24
+ react: 'React',
25
+ 'react-dom': 'ReactDOM',
26
+ },
27
+ },
28
+ },
29
+ },
30
+ server: {
31
+ port: 3000,
32
+ },
33
+ });