@dune2/tools 0.5.2 → 0.7.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/package.json +5 -5
- package/src/factory/createStateContext.tsx +1 -0
- package/src/storage/index.ts +53 -4
- package/src/store/index.ts +88 -0
- package/src/valtio/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dune2/tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n"
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"./valtio": "./src/valtio/index.ts",
|
|
33
33
|
"./valtio/*": "./src/valtio/*.ts",
|
|
34
34
|
"./niceModal": "./src/niceModal/index.tsx",
|
|
35
|
+
"./store": "./src/store/index.ts",
|
|
35
36
|
"./package.json": "./package.json"
|
|
36
37
|
},
|
|
37
38
|
"files": [
|
|
@@ -46,16 +47,15 @@
|
|
|
46
47
|
"bignumber.js": "^9.1.2",
|
|
47
48
|
"js-cookie": "^3.0.5",
|
|
48
49
|
"lodash": "^4",
|
|
49
|
-
"store2": "^2.14.3"
|
|
50
|
-
"valtio": "^1.13.2"
|
|
50
|
+
"store2": "^2.14.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@tanstack/react-query": "5.51.11",
|
|
54
54
|
"@types/js-cookie": "3.0.3",
|
|
55
55
|
"@types/lodash": "^4.17.0",
|
|
56
|
-
"@types/react": "^18.2.6",
|
|
57
56
|
"axios": "^1.6.8",
|
|
58
|
-
"react": "^
|
|
57
|
+
"react": "^19",
|
|
58
|
+
"valtio": "^2",
|
|
59
59
|
"zx": "^7.0.8"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
package/src/storage/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
1
3
|
import type { StoreType } from "store2";
|
|
2
4
|
import baseStore from "store2";
|
|
3
5
|
|
|
@@ -31,6 +33,13 @@ class StorageHelper<V = any> {
|
|
|
31
33
|
* 获取到的 key = "test.token"
|
|
32
34
|
*/
|
|
33
35
|
key: string;
|
|
36
|
+
/**
|
|
37
|
+
* 用来缓存当前值
|
|
38
|
+
* - 防止值是 object 的时候,每次 get 都都返回新的对象,导致 react 的 重复渲染
|
|
39
|
+
* - 在 set 时,如果值没有发生变化,则不触发 storage 事件
|
|
40
|
+
* - 在 get 时,如果值没有发生变化,则直接返回缓存的值
|
|
41
|
+
*/
|
|
42
|
+
private currentValue: V | undefined = undefined;
|
|
34
43
|
constructor(
|
|
35
44
|
public store: StoreType,
|
|
36
45
|
public namespace: string,
|
|
@@ -38,22 +47,62 @@ class StorageHelper<V = any> {
|
|
|
38
47
|
public defaultValue: V,
|
|
39
48
|
) {
|
|
40
49
|
this.key = `${namespace}.${baseKey}`;
|
|
50
|
+
this.currentValue = this.defaultValue;
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
get(): V | undefined {
|
|
44
|
-
|
|
54
|
+
const r = this.store.get(this.baseKey) ?? this.defaultValue;
|
|
55
|
+
if (!_.isEqual(r, this.currentValue)) {
|
|
56
|
+
this.currentValue = r;
|
|
57
|
+
}
|
|
58
|
+
return this.currentValue;
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
/**
|
|
48
62
|
* 设置为 undefined 时,会删除该 key
|
|
49
63
|
*/
|
|
50
|
-
set(v: V): void {
|
|
51
|
-
|
|
64
|
+
set(v: V | undefined): void {
|
|
65
|
+
if (_.isEqual(v, this.currentValue)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.currentValue = v;
|
|
69
|
+
v === undefined
|
|
70
|
+
? this.store.remove(this.baseKey)
|
|
71
|
+
: this.store.set(this.baseKey, v);
|
|
72
|
+
if (typeof window !== "undefined") {
|
|
73
|
+
// On localStorage.setItem, the storage event is only triggered on other tabs and windows.
|
|
74
|
+
// So we manually dispatch a storage event to trigger the subscribe function on the current window as well.
|
|
75
|
+
window.dispatchEvent(
|
|
76
|
+
new StorageEvent("storage", {
|
|
77
|
+
key: this.key,
|
|
78
|
+
// 这里 value 不重要,在内部会使用 get 重新获取值
|
|
79
|
+
newValue: null,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
52
83
|
}
|
|
53
84
|
|
|
54
85
|
remove(): void {
|
|
55
|
-
this.
|
|
86
|
+
this.set(undefined);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 这是 react hooks 的 useValue 的实现
|
|
91
|
+
*/
|
|
92
|
+
useValue() {
|
|
93
|
+
return useSyncExternalStore(
|
|
94
|
+
this.useSyncExternalStoreSubscribe,
|
|
95
|
+
this.useSyncExternalStoreGetSnapshot,
|
|
96
|
+
this.useSyncExternalStoreGetSnapshot,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
private useSyncExternalStoreSubscribe(listener: () => void) {
|
|
100
|
+
window.addEventListener("storage", listener);
|
|
101
|
+
return () => {
|
|
102
|
+
window.removeEventListener("storage", listener);
|
|
103
|
+
};
|
|
56
104
|
}
|
|
105
|
+
private useSyncExternalStoreGetSnapshot = this.get.bind(this);
|
|
57
106
|
}
|
|
58
107
|
|
|
59
108
|
/**
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { useDebugValue, useRef, useSyncExternalStore } from "react";
|
|
3
|
+
import { proxy, snapshot, subscribe, useSnapshot } from "valtio";
|
|
4
|
+
|
|
5
|
+
const stores: any = {};
|
|
6
|
+
|
|
7
|
+
if (typeof window !== "undefined") {
|
|
8
|
+
Object.defineProperty(window, "__stores2", {
|
|
9
|
+
get() {
|
|
10
|
+
const r = snapshot(proxy(stores));
|
|
11
|
+
return { ...r, __raw: stores };
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type Config<S extends object, A extends Record<string, Action<S, any>>> = {
|
|
17
|
+
name: string;
|
|
18
|
+
state: S;
|
|
19
|
+
actionsCreator: (state: S) => A;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function createStore<
|
|
23
|
+
S extends object,
|
|
24
|
+
A extends Record<string, Action<S, any>>,
|
|
25
|
+
>(config: Config<S, A>) {
|
|
26
|
+
const store = proxy(config.state);
|
|
27
|
+
const initialState = snapshot(store) as S;
|
|
28
|
+
|
|
29
|
+
const actions = config.actionsCreator(store);
|
|
30
|
+
|
|
31
|
+
const api = {
|
|
32
|
+
/**
|
|
33
|
+
* 状态 可以直接修改,会触发状态变化
|
|
34
|
+
*/
|
|
35
|
+
state: store,
|
|
36
|
+
/**
|
|
37
|
+
* 初始状态 快照
|
|
38
|
+
*/
|
|
39
|
+
initialState,
|
|
40
|
+
/**
|
|
41
|
+
* 获取当前状态 快照
|
|
42
|
+
*/
|
|
43
|
+
getState() {
|
|
44
|
+
return snapshot(store) as S;
|
|
45
|
+
},
|
|
46
|
+
actions,
|
|
47
|
+
/**
|
|
48
|
+
* 监听状态变化
|
|
49
|
+
*/
|
|
50
|
+
subscribe(fn: (state: S) => void) {
|
|
51
|
+
return subscribe(store, (ops) => {
|
|
52
|
+
const state = api.getState();
|
|
53
|
+
fn(state);
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* 获取当前状态 快照
|
|
58
|
+
*/
|
|
59
|
+
useSnapshot() {
|
|
60
|
+
return useSnapshot(store) as S;
|
|
61
|
+
},
|
|
62
|
+
useShallowSnapshot<T>(selector: (state: S) => T) {
|
|
63
|
+
const prev = useRef<T>(undefined);
|
|
64
|
+
const combinedSelector = (state: S) => {
|
|
65
|
+
const next = selector(state);
|
|
66
|
+
if (!_.isEqual(prev.current, next)) {
|
|
67
|
+
prev.current = next;
|
|
68
|
+
}
|
|
69
|
+
return prev.current;
|
|
70
|
+
};
|
|
71
|
+
const slice = useSyncExternalStore(
|
|
72
|
+
api.subscribe,
|
|
73
|
+
() => {
|
|
74
|
+
return combinedSelector(api.getState());
|
|
75
|
+
},
|
|
76
|
+
() => {
|
|
77
|
+
return combinedSelector(api.initialState);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
useDebugValue(slice);
|
|
81
|
+
return slice;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return api;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
type Action<S, T> = (payload: T) => any;
|