@choksheak/react-utils 0.3.6 → 0.3.8
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/NoSSR.cjs +37 -0
- package/NoSSR.d.mts +24 -0
- package/NoSSR.d.ts +20 -0
- package/NoSSR.min.cjs +2 -0
- package/NoSSR.min.cjs.map +1 -0
- package/NoSSR.min.mjs +2 -0
- package/NoSSR.min.mjs.map +1 -0
- package/NoSSR.mjs +12 -0
- package/README.md +10 -1
- package/fetcher.cjs +3 -3
- package/fetcher.d.mts +5 -4
- package/fetcher.d.ts +4 -3
- package/fetcher.min.cjs +1 -1
- package/fetcher.min.cjs.map +1 -1
- package/fetcher.min.mjs +1 -1
- package/fetcher.min.mjs.map +1 -1
- package/fetcher.mjs +2 -2
- package/package.json +21 -3
- package/sharedQuery.cjs +486 -486
- package/sharedQuery.d.mts +64 -61
- package/sharedQuery.d.ts +62 -59
- package/sharedQuery.min.cjs +1 -1
- package/sharedQuery.min.cjs.map +1 -1
- package/sharedQuery.min.mjs +1 -1
- package/sharedQuery.min.mjs.map +1 -1
- package/sharedQuery.mjs +487 -486
- package/sharedState.cjs +201 -199
- package/sharedState.d.mts +48 -38
- package/sharedState.d.ts +45 -36
- package/sharedState.min.cjs +1 -1
- package/sharedState.min.cjs.map +1 -1
- package/sharedState.min.mjs +1 -1
- package/sharedState.min.mjs.map +1 -1
- package/sharedState.mjs +202 -199
- package/storage.cjs +79 -65
- package/storage.d.mts +39 -2
- package/storage.d.ts +37 -1
- package/storage.min.cjs +1 -1
- package/storage.min.cjs.map +1 -1
- package/storage.min.mjs +1 -1
- package/storage.min.mjs.map +1 -1
- package/storage.mjs +77 -65
- package/stringify.d.mts +9 -0
- package/stringify.d.ts +9 -0
- package/stringify.min.cjs.map +1 -1
- package/stringify.min.mjs.map +1 -1
- package/useDeepMemo.cjs +1 -1
- package/useDeepMemo.d.mts +11 -0
- package/useDeepMemo.d.ts +11 -0
- package/useDeepMemo.min.cjs +1 -1
- package/useDeepMemo.min.cjs.map +1 -1
- package/useDeepMemo.min.mjs +1 -1
- package/useDeepMemo.min.mjs.map +1 -1
- package/useDeepMemo.mjs +1 -1
package/sharedState.cjs
CHANGED
|
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/sharedState.ts
|
|
21
21
|
var sharedState_exports = {};
|
|
22
22
|
__export(sharedState_exports, {
|
|
23
|
-
SharedState: () => SharedState,
|
|
24
23
|
SharedStateConfig: () => SharedStateConfig,
|
|
25
24
|
configureSharedState: () => configureSharedState,
|
|
26
25
|
sharedState: () => sharedState,
|
|
@@ -43,70 +42,12 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
43
42
|
}
|
|
44
43
|
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
45
44
|
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
46
|
-
const
|
|
45
|
+
const config = { ...store, expiryMs };
|
|
47
46
|
switch (store.persistTo) {
|
|
48
|
-
case "localStorage":
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
set: (key, value) => {
|
|
54
|
-
if (IS_NOT_BROWSER) return;
|
|
55
|
-
adapter.set(key, value);
|
|
56
|
-
},
|
|
57
|
-
get: (key) => {
|
|
58
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
59
|
-
const value = adapter.get(key);
|
|
60
|
-
if (isValid && !isValid(value)) {
|
|
61
|
-
console.warn(
|
|
62
|
-
`localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
63
|
-
);
|
|
64
|
-
adapter.delete(key);
|
|
65
|
-
return void 0;
|
|
66
|
-
}
|
|
67
|
-
return value;
|
|
68
|
-
},
|
|
69
|
-
delete: (key) => {
|
|
70
|
-
if (IS_NOT_BROWSER) return;
|
|
71
|
-
adapter.delete(key);
|
|
72
|
-
},
|
|
73
|
-
clear: () => {
|
|
74
|
-
if (IS_NOT_BROWSER) return;
|
|
75
|
-
adapter.clear();
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
case "indexedDb": {
|
|
80
|
-
const keyPrefix = store.key + ":";
|
|
81
|
-
const isValid2 = store.isValid;
|
|
82
|
-
return {
|
|
83
|
-
set: async (key, value) => {
|
|
84
|
-
if (IS_NOT_BROWSER) return;
|
|
85
|
-
await import_kvStore.kvStore.set(keyPrefix + key, value, expiryMs);
|
|
86
|
-
},
|
|
87
|
-
get: async (key) => {
|
|
88
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
89
|
-
const fullKey = keyPrefix + key;
|
|
90
|
-
const value = await import_kvStore.kvStore.get(fullKey);
|
|
91
|
-
if (isValid2 && !isValid2(value)) {
|
|
92
|
-
console.warn(
|
|
93
|
-
`indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
94
|
-
);
|
|
95
|
-
await import_kvStore.kvStore.delete(fullKey);
|
|
96
|
-
return void 0;
|
|
97
|
-
}
|
|
98
|
-
return value;
|
|
99
|
-
},
|
|
100
|
-
delete: async (key) => {
|
|
101
|
-
if (IS_NOT_BROWSER) return;
|
|
102
|
-
await import_kvStore.kvStore.delete(keyPrefix + key);
|
|
103
|
-
},
|
|
104
|
-
clear: async () => {
|
|
105
|
-
if (IS_NOT_BROWSER) return;
|
|
106
|
-
await import_kvStore.kvStore.clear();
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
}
|
|
47
|
+
case "localStorage":
|
|
48
|
+
return localStoreAdapter(config);
|
|
49
|
+
case "indexedDb":
|
|
50
|
+
return kvStoreAdapter(config);
|
|
110
51
|
default:
|
|
111
52
|
store.persistTo;
|
|
112
53
|
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
@@ -114,6 +55,74 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
114
55
|
}
|
|
115
56
|
return store;
|
|
116
57
|
}
|
|
58
|
+
function localStoreAdapter({
|
|
59
|
+
key,
|
|
60
|
+
expiryMs,
|
|
61
|
+
isValid
|
|
62
|
+
}) {
|
|
63
|
+
const adapter = (0, import_localStore.createLocalStore)(key, { defaultExpiryMs: expiryMs });
|
|
64
|
+
return {
|
|
65
|
+
set: (key2, value) => {
|
|
66
|
+
if (IS_NOT_BROWSER) return;
|
|
67
|
+
adapter.set(key2, value);
|
|
68
|
+
},
|
|
69
|
+
get: (key2) => {
|
|
70
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
71
|
+
const value = adapter.get(key2);
|
|
72
|
+
if (isValid && !isValid(value)) {
|
|
73
|
+
console.warn(
|
|
74
|
+
`localStorage adapter: Auto-discard invalid value for ${key2}: ${JSON.stringify(value)}`
|
|
75
|
+
);
|
|
76
|
+
adapter.delete(key2);
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
},
|
|
81
|
+
delete: (key2) => {
|
|
82
|
+
if (IS_NOT_BROWSER) return;
|
|
83
|
+
adapter.delete(key2);
|
|
84
|
+
},
|
|
85
|
+
clear: () => {
|
|
86
|
+
if (IS_NOT_BROWSER) return;
|
|
87
|
+
adapter.clear();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function kvStoreAdapter({
|
|
92
|
+
key,
|
|
93
|
+
expiryMs,
|
|
94
|
+
isValid
|
|
95
|
+
}) {
|
|
96
|
+
const keyPrefix = key + ":";
|
|
97
|
+
return {
|
|
98
|
+
set: async (key2, value) => {
|
|
99
|
+
if (IS_NOT_BROWSER) return;
|
|
100
|
+
await import_kvStore.kvStore.set(keyPrefix + key2, value, expiryMs);
|
|
101
|
+
},
|
|
102
|
+
get: async (key2) => {
|
|
103
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
104
|
+
const fullKey = keyPrefix + key2;
|
|
105
|
+
const value = await import_kvStore.kvStore.get(fullKey);
|
|
106
|
+
if (value !== void 0 && isValid && !isValid(value)) {
|
|
107
|
+
console.warn(
|
|
108
|
+
`indexedDb adapter: Auto-discard invalid value for ${key2}:`,
|
|
109
|
+
value
|
|
110
|
+
);
|
|
111
|
+
await import_kvStore.kvStore.delete(fullKey);
|
|
112
|
+
return void 0;
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
},
|
|
116
|
+
delete: async (key2) => {
|
|
117
|
+
if (IS_NOT_BROWSER) return;
|
|
118
|
+
await import_kvStore.kvStore.delete(keyPrefix + key2);
|
|
119
|
+
},
|
|
120
|
+
clear: async () => {
|
|
121
|
+
if (IS_NOT_BROWSER) return;
|
|
122
|
+
await import_kvStore.kvStore.clear();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
117
126
|
|
|
118
127
|
// src/sharedState.ts
|
|
119
128
|
var SharedStateConfig = {
|
|
@@ -131,41 +140,11 @@ var SharedStateConfig = {
|
|
|
131
140
|
function configureSharedState(config) {
|
|
132
141
|
Object.assign(SharedStateConfig, config);
|
|
133
142
|
}
|
|
134
|
-
|
|
135
|
-
dataByKey = /* @__PURE__ */ new Map();
|
|
136
|
-
subscribersByKey = /* @__PURE__ */ new Map();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
get(key) {
|
|
140
|
-
return this.dataByKey.get(key);
|
|
141
|
-
}
|
|
142
|
-
set(key, value) {
|
|
143
|
-
const prev = this.dataByKey.get(key);
|
|
144
|
-
if (prev === value) return;
|
|
145
|
-
this.dataByKey.set(key, value);
|
|
146
|
-
this.notify(key, value, prev);
|
|
147
|
-
}
|
|
148
|
-
delete(key) {
|
|
149
|
-
this.dataByKey.delete(key);
|
|
150
|
-
}
|
|
151
|
-
setNoNotify(key, value) {
|
|
152
|
-
this.dataByKey.set(key, value);
|
|
153
|
-
}
|
|
154
|
-
// Subscribe to changes of a specific key
|
|
155
|
-
subscribe(key, subscriber) {
|
|
156
|
-
let subscribers = this.subscribersByKey.get(key);
|
|
157
|
-
if (!subscribers) {
|
|
158
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
159
|
-
this.subscribersByKey.set(key, subscribers);
|
|
160
|
-
}
|
|
161
|
-
subscribers.add(subscriber);
|
|
162
|
-
return () => {
|
|
163
|
-
subscribers.delete(subscriber);
|
|
164
|
-
if (subscribers.size === 0) this.subscribersByKey.delete(key);
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
notify(key, next, prev) {
|
|
168
|
-
const subscribers = this.subscribersByKey.get(key);
|
|
143
|
+
function createPubSubStore() {
|
|
144
|
+
const dataByKey = /* @__PURE__ */ new Map();
|
|
145
|
+
const subscribersByKey = /* @__PURE__ */ new Map();
|
|
146
|
+
function notify(key, next, prev) {
|
|
147
|
+
const subscribers = subscribersByKey.get(key);
|
|
169
148
|
if (!subscribers) return;
|
|
170
149
|
const copy = Array.from(subscribers.values());
|
|
171
150
|
for (const subscriber of copy) {
|
|
@@ -176,126 +155,149 @@ var PubSubStore = class {
|
|
|
176
155
|
}
|
|
177
156
|
}
|
|
178
157
|
}
|
|
179
|
-
|
|
180
|
-
|
|
158
|
+
return {
|
|
159
|
+
// This could be undefined if T includes undefined, else it should never
|
|
160
|
+
// return undefined because the user has to always specify the default value.
|
|
161
|
+
get(key) {
|
|
162
|
+
return dataByKey.get(key);
|
|
163
|
+
},
|
|
164
|
+
set(key, value) {
|
|
165
|
+
const prev = dataByKey.get(key);
|
|
166
|
+
if (prev === value) return;
|
|
167
|
+
dataByKey.set(key, value);
|
|
168
|
+
notify(key, value, prev);
|
|
169
|
+
},
|
|
170
|
+
delete(key) {
|
|
171
|
+
dataByKey.delete(key);
|
|
172
|
+
},
|
|
173
|
+
setNoNotify(key, value) {
|
|
174
|
+
dataByKey.set(key, value);
|
|
175
|
+
},
|
|
176
|
+
// Subscribe to changes of a specific key
|
|
177
|
+
subscribe(key, subscriber) {
|
|
178
|
+
let subscribers = subscribersByKey.get(key);
|
|
179
|
+
if (!subscribers) {
|
|
180
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
181
|
+
subscribersByKey.set(key, subscribers);
|
|
182
|
+
}
|
|
183
|
+
subscribers.add(subscriber);
|
|
184
|
+
return () => {
|
|
185
|
+
subscribers.delete(subscriber);
|
|
186
|
+
if (subscribers.size === 0) subscribersByKey.delete(key);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
var pubSubStore = createPubSubStore();
|
|
181
192
|
var STORAGE_KEY = "state";
|
|
182
193
|
var stateKey = 0;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
194
|
+
function sharedState(defaultValue, options) {
|
|
195
|
+
let readyResolve = () => {
|
|
196
|
+
};
|
|
197
|
+
const readyPromise = new Promise((resolve) => {
|
|
198
|
+
readyResolve = resolve;
|
|
199
|
+
});
|
|
200
|
+
const resolveReadyPromise = readyResolve;
|
|
201
|
+
const pubSubKey = String(stateKey++);
|
|
202
|
+
const storageAdapter = getStorageAdapter(
|
|
203
|
+
options,
|
|
204
|
+
SharedStateConfig.storeExpiryMs
|
|
205
|
+
);
|
|
206
|
+
pubSubStore.setNoNotify(pubSubKey, defaultValue);
|
|
207
|
+
const lazyLoad = options?.lazyLoad;
|
|
208
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
209
|
+
const obj = {
|
|
210
|
+
/** Allow users to await for the completion of data init. */
|
|
211
|
+
readyPromise,
|
|
212
|
+
lazy,
|
|
213
|
+
initStarted: false,
|
|
214
|
+
initDone: false,
|
|
215
|
+
/** `subscribe` function required by useSyncExternalStore. */
|
|
216
|
+
subscribe(subscriber) {
|
|
217
|
+
return pubSubStore.subscribe(pubSubKey, () => subscriber());
|
|
218
|
+
},
|
|
219
|
+
/** Returns true if there is a backing store. */
|
|
220
|
+
hasStore() {
|
|
221
|
+
return Boolean(storageAdapter);
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Set a value directly into the store. Does nothing if there is no
|
|
225
|
+
* backing store.
|
|
226
|
+
*
|
|
227
|
+
* Warning: If you want to set the value properly, you should use
|
|
228
|
+
* `setValue()` instead which also updates the in-memory copy.
|
|
229
|
+
*/
|
|
230
|
+
async setInStore(value) {
|
|
231
|
+
return await storageAdapter?.set(STORAGE_KEY, value);
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Get a value directly from the store. Returns undefined if there is no
|
|
235
|
+
* backing store.
|
|
236
|
+
*/
|
|
237
|
+
async getFromStore() {
|
|
238
|
+
return await storageAdapter?.get(STORAGE_KEY);
|
|
239
|
+
},
|
|
240
|
+
/**
|
|
241
|
+
* Get the value of the shared state.
|
|
242
|
+
* `getSnapshot` function — must return same ref if value unchanged.
|
|
243
|
+
* Note that this would return the default value if data init from store is
|
|
244
|
+
* not completed yet.
|
|
245
|
+
*/
|
|
246
|
+
getSnapshot() {
|
|
247
|
+
return pubSubStore.get(pubSubKey);
|
|
248
|
+
},
|
|
249
|
+
/** Set the value of the shared state. */
|
|
250
|
+
setValue(next) {
|
|
251
|
+
if (typeof next === "function") {
|
|
252
|
+
const prev = pubSubStore.get(pubSubKey);
|
|
253
|
+
next = next(prev);
|
|
254
|
+
}
|
|
255
|
+
pubSubStore.set(pubSubKey, next);
|
|
256
|
+
void obj.setInStore(next);
|
|
257
|
+
},
|
|
258
|
+
/**
|
|
259
|
+
* Remove the value of the shared state. Actually this just sets the value
|
|
260
|
+
* back to the given default value.
|
|
261
|
+
*/
|
|
262
|
+
delete() {
|
|
263
|
+
pubSubStore.set(pubSubKey, defaultValue);
|
|
264
|
+
void storageAdapter?.set(STORAGE_KEY, defaultValue);
|
|
265
|
+
},
|
|
266
|
+
/** Initial default handling. */
|
|
267
|
+
async initDefaultValueOnce() {
|
|
268
|
+
if (obj.initDone || obj.initStarted) return;
|
|
269
|
+
obj.initStarted = true;
|
|
270
|
+
const stored = await obj.getFromStore();
|
|
271
|
+
const value = stored !== void 0 ? stored : defaultValue;
|
|
272
|
+
pubSubStore.set(pubSubKey, value);
|
|
273
|
+
obj.initDone = true;
|
|
274
|
+
resolveReadyPromise();
|
|
254
275
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Remove the value of the shared state. Actually this just sets the value
|
|
260
|
-
* back to the given default value.
|
|
261
|
-
*/
|
|
262
|
-
delete() {
|
|
263
|
-
pubSubStore.set(this.pubSubKey, this.defaultValue);
|
|
264
|
-
void this.storageAdapter?.set(STORAGE_KEY, this.defaultValue);
|
|
265
|
-
}
|
|
266
|
-
/** Initial default handling. */
|
|
267
|
-
async initDefaultValueOnce() {
|
|
268
|
-
if (this.initDone || this.initStarted) return;
|
|
269
|
-
this.initStarted = true;
|
|
270
|
-
const stored = await this.getFromStore();
|
|
271
|
-
const value = stored !== void 0 ? stored : this.defaultValue;
|
|
272
|
-
pubSubStore.set(this.pubSubKey, value);
|
|
273
|
-
this.initDone = true;
|
|
274
|
-
this.resolveReadyPromise();
|
|
276
|
+
};
|
|
277
|
+
if (!lazy) {
|
|
278
|
+
void obj.initDefaultValueOnce();
|
|
275
279
|
}
|
|
276
|
-
|
|
277
|
-
function sharedState(defaultValue, options) {
|
|
278
|
-
return new SharedState(defaultValue, options);
|
|
280
|
+
return obj;
|
|
279
281
|
}
|
|
280
282
|
function useSharedState(state) {
|
|
281
|
-
state.initDefaultValueOnce();
|
|
283
|
+
void state.initDefaultValueOnce();
|
|
282
284
|
const value = (0, import_react.useSyncExternalStore)(
|
|
283
285
|
(onStoreChange) => state.subscribe(onStoreChange),
|
|
284
286
|
() => state.getSnapshot(),
|
|
285
287
|
// getServerSnapshot is needed to prevent SSR dev errors in nextjs.
|
|
286
288
|
() => state.getSnapshot()
|
|
287
289
|
);
|
|
288
|
-
return [value, state.
|
|
290
|
+
return [value, state.setValue];
|
|
289
291
|
}
|
|
290
292
|
function useSharedStateValue(state) {
|
|
291
293
|
return useSharedState(state)[0];
|
|
292
294
|
}
|
|
293
295
|
function useSharedStateSetter(state) {
|
|
294
|
-
|
|
296
|
+
void state.initDefaultValueOnce();
|
|
297
|
+
return state.setValue;
|
|
295
298
|
}
|
|
296
299
|
// Annotate the CommonJS export names for ESM import in node:
|
|
297
300
|
0 && (module.exports = {
|
|
298
|
-
SharedState,
|
|
299
301
|
SharedStateConfig,
|
|
300
302
|
configureSharedState,
|
|
301
303
|
sharedState,
|
package/sharedState.d.mts
CHANGED
|
@@ -1,18 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SetStateAction, Dispatch } from 'react';
|
|
2
2
|
import { StorageOptions } from './storage.mjs';
|
|
3
|
+
import '@choksheak/ts-utils/duration';
|
|
3
4
|
import '@choksheak/ts-utils/storageAdapter';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @packageDocumentation
|
|
7
8
|
*
|
|
8
9
|
* The "shared state" is a React state that shares the same value across the
|
|
9
|
-
* entire app.
|
|
10
|
-
*
|
|
11
|
-
* indexed db, with an automatic expiration for persisted values.
|
|
10
|
+
* entire app. Users declare a shared state constant that can be reused across
|
|
11
|
+
* multiple components with the same underlying value.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Shared states provide persistence to local storage and/or indexed DB out of
|
|
14
|
+
* the box, by using "@choksheak/ts-utils". Note that no matter if the shared
|
|
15
|
+
* state uses persistence or not, it will always maintain a copy of the state
|
|
16
|
+
* in memory. Persisted values can be configured with an automatic expiration
|
|
17
|
+
* duration.
|
|
18
|
+
*
|
|
19
|
+
* This module is largely similar to the open source "jotai" npm library. You
|
|
20
|
+
* can use shared states to replace "jotai" in your code, and optionally make
|
|
21
|
+
* use of the persistence mechanism. A quick migration guide from jotai is:
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
24
|
+
* atom() --> sharedState()
|
|
25
|
+
* useAtom() --> useSharedState()
|
|
26
|
+
* useAtomValue() --> useSharedStateValue()
|
|
27
|
+
* useSetAtom() --> useSharedStateSetter()
|
|
28
|
+
* ```
|
|
16
29
|
*
|
|
17
30
|
* Example:
|
|
18
31
|
* ```
|
|
@@ -22,7 +35,9 @@ import '@choksheak/ts-utils/storageAdapter';
|
|
|
22
35
|
* // The first argument is the default value, which is required.
|
|
23
36
|
* // The second argument is an options object, and is optional (defaults
|
|
24
37
|
* // to storing in memory only without any persistence).
|
|
25
|
-
* const usersState = sharedState(
|
|
38
|
+
* const usersState = sharedState(
|
|
39
|
+
* [], { store: { persistTo: "indexedDb", key: "users" }}
|
|
40
|
+
* );
|
|
26
41
|
*
|
|
27
42
|
* export const UsersComponent: React.FC = () => {
|
|
28
43
|
* // Get both the value and the setter.
|
|
@@ -68,21 +83,24 @@ declare function configureSharedState(config: Partial<SharedStateConfig>): void;
|
|
|
68
83
|
type SharedStateOptions<T> = {
|
|
69
84
|
lazyLoad?: boolean;
|
|
70
85
|
} & StorageOptions<T>;
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Create a new shared state object. Put this code at the top level scope:
|
|
88
|
+
*
|
|
89
|
+
* Example:
|
|
90
|
+
* ```
|
|
91
|
+
* const counterState = sharedState<number>(0);
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function sharedState<T>(defaultValue: T, options?: SharedStateOptions<T>): {
|
|
95
|
+
/** Allow users to await for the completion of data init. */
|
|
79
96
|
readonly readyPromise: Promise<void>;
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
readonly lazy: boolean;
|
|
98
|
+
readonly initStarted: false;
|
|
99
|
+
readonly initDone: false;
|
|
82
100
|
/** `subscribe` function required by useSyncExternalStore. */
|
|
83
|
-
subscribe(subscriber: () => void)
|
|
101
|
+
readonly subscribe: (subscriber: () => void) => () => void;
|
|
84
102
|
/** Returns true if there is a backing store. */
|
|
85
|
-
hasStore()
|
|
103
|
+
readonly hasStore: () => boolean;
|
|
86
104
|
/**
|
|
87
105
|
* Set a value directly into the store. Does nothing if there is no
|
|
88
106
|
* backing store.
|
|
@@ -90,38 +108,30 @@ declare class SharedState<T> {
|
|
|
90
108
|
* Warning: If you want to set the value properly, you should use
|
|
91
109
|
* `setValue()` instead which also updates the in-memory copy.
|
|
92
110
|
*/
|
|
93
|
-
setInStore(value: T)
|
|
111
|
+
readonly setInStore: (value: T) => Promise<void>;
|
|
94
112
|
/**
|
|
95
113
|
* Get a value directly from the store. Returns undefined if there is no
|
|
96
114
|
* backing store.
|
|
97
115
|
*/
|
|
98
|
-
getFromStore()
|
|
116
|
+
readonly getFromStore: () => Promise<T | undefined>;
|
|
99
117
|
/**
|
|
100
118
|
* Get the value of the shared state.
|
|
101
119
|
* `getSnapshot` function — must return same ref if value unchanged.
|
|
102
120
|
* Note that this would return the default value if data init from store is
|
|
103
121
|
* not completed yet.
|
|
104
122
|
*/
|
|
105
|
-
getSnapshot()
|
|
123
|
+
readonly getSnapshot: () => T;
|
|
106
124
|
/** Set the value of the shared state. */
|
|
107
|
-
setValue(next: SetStateAction<T>)
|
|
125
|
+
readonly setValue: (next: SetStateAction<T>) => void;
|
|
108
126
|
/**
|
|
109
127
|
* Remove the value of the shared state. Actually this just sets the value
|
|
110
128
|
* back to the given default value.
|
|
111
129
|
*/
|
|
112
|
-
delete()
|
|
130
|
+
readonly delete: () => void;
|
|
113
131
|
/** Initial default handling. */
|
|
114
|
-
initDefaultValueOnce()
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
* Create a new shared state object. Put this code at the top level scope:
|
|
118
|
-
*
|
|
119
|
-
* Example:
|
|
120
|
-
* ```
|
|
121
|
-
* const counterState = sharedState<number>(0);
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
|
-
declare function sharedState<T>(defaultValue: T, options?: SharedStateOptions<T>): SharedState<T>;
|
|
132
|
+
readonly initDefaultValueOnce: () => Promise<void>;
|
|
133
|
+
};
|
|
134
|
+
type SharedState<T> = ReturnType<typeof sharedState<T>>;
|
|
125
135
|
/**
|
|
126
136
|
* React hook for subscribing to a key in the global store.
|
|
127
137
|
*
|
|
@@ -148,6 +158,6 @@ declare function useSharedStateValue<T>(state: SharedState<T>): T;
|
|
|
148
158
|
* const setCounter = useSharedStateSetter(counterState);
|
|
149
159
|
* ```
|
|
150
160
|
*/
|
|
151
|
-
declare function useSharedStateSetter<T>(state: SharedState<T>):
|
|
161
|
+
declare function useSharedStateSetter<T>(state: SharedState<T>): (next: SetStateAction<T>) => void;
|
|
152
162
|
|
|
153
|
-
export { SharedState, SharedStateConfig, type SharedStateOptions, configureSharedState, sharedState, useSharedState, useSharedStateSetter, useSharedStateValue };
|
|
163
|
+
export { type SharedState, SharedStateConfig, type SharedStateOptions, configureSharedState, sharedState, useSharedState, useSharedStateSetter, useSharedStateValue };
|