dev-react-microstore 4.0.1 → 5.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.
- package/README.md +101 -2
- package/dist/index.d.mts +85 -2
- package/dist/index.d.ts +85 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/example/README.md +54 -0
- package/example/eslint.config.js +28 -0
- package/example/index.html +13 -0
- package/example/package-lock.json +3382 -0
- package/example/package.json +29 -0
- package/example/public/index.html +98 -0
- package/example/public/vite.svg +1 -0
- package/example/src/App.css +613 -0
- package/example/src/App.tsx +34 -0
- package/example/src/assets/react.svg +1 -0
- package/example/src/components/Counter.tsx +112 -0
- package/example/src/components/CustomCompare.tsx +466 -0
- package/example/src/components/Logs.tsx +28 -0
- package/example/src/components/Search.tsx +38 -0
- package/example/src/components/ThemeToggle.tsx +25 -0
- package/example/src/components/TodoList.tsx +63 -0
- package/example/src/components/UserManager.tsx +68 -0
- package/example/src/index.css +68 -0
- package/example/src/main.tsx +10 -0
- package/example/src/store.ts +223 -0
- package/example/src/vite-env.d.ts +1 -0
- package/example/tsconfig.app.json +26 -0
- package/example/tsconfig.json +7 -0
- package/example/tsconfig.node.json +25 -0
- package/example/vite.config.ts +7 -0
- package/package.json +10 -3
- package/src/index.ts +245 -8
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A minimal global state manager for React with fine-grained subscriptions.
|
|
|
8
8
|
npm install react-microstore
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Usage
|
|
11
|
+
## Basic Usage
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
14
|
import { createStoreState, useStoreSelector } from 'react-microstore';
|
|
@@ -29,6 +29,102 @@ function Counter() {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Middleware Support
|
|
33
|
+
|
|
34
|
+
Add Express-style middleware to intercept and control state updates:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
const store = createStoreState({ count: 0, user: null });
|
|
38
|
+
|
|
39
|
+
// Validation middleware - can block updates
|
|
40
|
+
store.addMiddleware(
|
|
41
|
+
(currentState, update, next) => {
|
|
42
|
+
if (update.count !== undefined && update.count < 0) {
|
|
43
|
+
// Don't call next() to block the update
|
|
44
|
+
console.log('Blocked negative count');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
next(); // Allow the update
|
|
48
|
+
},
|
|
49
|
+
['count'] // Only run for count updates
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Transform middleware - can modify updates
|
|
53
|
+
store.addMiddleware(
|
|
54
|
+
(currentState, update, next) => {
|
|
55
|
+
if (update.user?.name) {
|
|
56
|
+
const modifiedUpdate = {
|
|
57
|
+
...update,
|
|
58
|
+
user: {
|
|
59
|
+
...update.user,
|
|
60
|
+
name: update.user.name.trim().toLowerCase()
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
next(modifiedUpdate); // Pass modified update
|
|
64
|
+
} else {
|
|
65
|
+
next(); // Pass original update
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Logging middleware
|
|
71
|
+
store.addMiddleware(
|
|
72
|
+
(currentState, update, next) => {
|
|
73
|
+
console.log('Processing update:', update);
|
|
74
|
+
next(); // Continue
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Persistence
|
|
80
|
+
|
|
81
|
+
The store supports automatic state persistence using middleware with per-key storage:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { createStoreState, createPersistenceMiddleware, loadPersistedState } from '@ohad/react-microstore'
|
|
85
|
+
|
|
86
|
+
// Load persisted state during initialization
|
|
87
|
+
const persistedState = loadPersistedState<AppState>(
|
|
88
|
+
localStorage,
|
|
89
|
+
'my-app-state',
|
|
90
|
+
['theme', 'userName', 'isLoggedIn']
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Create store with merged initial + persisted state
|
|
94
|
+
const store = createStoreState<AppState>({
|
|
95
|
+
theme: 'light',
|
|
96
|
+
userName: '',
|
|
97
|
+
isLoggedIn: false,
|
|
98
|
+
tempData: { cache: [] },
|
|
99
|
+
...persistedState // Apply persisted values
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Add persistence middleware - saves each key individually
|
|
103
|
+
store.addMiddleware(
|
|
104
|
+
createPersistenceMiddleware(localStorage, 'my-app-state', ['theme', 'userName', 'isLoggedIn'])
|
|
105
|
+
);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Key benefits:**
|
|
109
|
+
|
|
110
|
+
✅ **Per-key storage** - Each key stored separately (e.g., `my-app-state:theme`, `my-app-state:userName`)
|
|
111
|
+
✅ **Efficient writes** - Only writes to storage when specified keys actually change
|
|
112
|
+
✅ **No state blobs** - Avoids serializing/storing entire state objects
|
|
113
|
+
✅ **Composable** - Uses the same middleware system as validation/logging
|
|
114
|
+
✅ **Flexible** - Easy to swap storage backends or add custom logic
|
|
115
|
+
|
|
116
|
+
## Debouncing
|
|
117
|
+
|
|
118
|
+
Control when updates are applied:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
// Debounce updates for 300ms
|
|
122
|
+
store.set({ searchQuery: 'new value' }, 300);
|
|
123
|
+
|
|
124
|
+
// Or use boolean for default debounce (0ms)
|
|
125
|
+
store.set({ count: count + 1 }, true);
|
|
126
|
+
```
|
|
127
|
+
|
|
32
128
|
## Custom Comparison Function
|
|
33
129
|
|
|
34
130
|
```tsx
|
|
@@ -143,7 +239,10 @@ function UserProfile() {
|
|
|
143
239
|
- Extremely lightweight (less than 2KB minified)
|
|
144
240
|
- Fine-grained subscriptions to minimize re-renders
|
|
145
241
|
- Custom comparison functions for complex state updates
|
|
146
|
-
-
|
|
242
|
+
- Simple middleware support with `addMiddleware()`
|
|
243
|
+
- Automatic persistence to localStorage/sessionStorage
|
|
244
|
+
- Debouncing support to control update frequency
|
|
245
|
+
- Fully Type-safe with TypeScript
|
|
147
246
|
- No dependencies other than React
|
|
148
247
|
- Update store from anywhere in your application
|
|
149
248
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
type StoreListener = () => void;
|
|
2
|
+
type MiddlewareFunction<T extends object> = (currentState: T, update: Partial<T>, next: (modifiedUpdate?: Partial<T>) => void) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new reactive store with fine-grained subscriptions and middleware support.
|
|
5
|
+
*
|
|
6
|
+
* @param initialState - The initial state object for the store
|
|
7
|
+
* @returns Store object with methods: get, set, subscribe, select, addMiddleware
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const store = createStoreState({ count: 0, name: 'John' });
|
|
12
|
+
* store.set({ count: 1 }); // Update state
|
|
13
|
+
* const { count } = useStoreSelector(store, ['count']); // Subscribe in React
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
2
16
|
declare function createStoreState<T extends object>(initialState: T): {
|
|
3
17
|
get: () => T;
|
|
4
|
-
set: (
|
|
18
|
+
set: (update: Partial<T>, debounceDelay?: number | boolean) => void;
|
|
5
19
|
subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
|
|
6
20
|
select: <K extends keyof T>(keys: K[]) => Pick<T, K>;
|
|
21
|
+
addMiddleware: (callbackOrTuple: MiddlewareFunction<T> | [MiddlewareFunction<T>, (keyof T)[]], affectedKeys?: (keyof T)[] | null) => () => void;
|
|
7
22
|
};
|
|
8
23
|
type StoreType<T extends object> = ReturnType<typeof createStoreState<T>>;
|
|
9
24
|
type PrimitiveKey<T extends object> = keyof T;
|
|
@@ -17,6 +32,74 @@ type ExtractSelectorKeys<T extends object, S extends SelectorInput<T>> = {
|
|
|
17
32
|
[K in S[number] extends infer Item ? Item extends keyof T ? Item : keyof Item : never]: T[K];
|
|
18
33
|
};
|
|
19
34
|
type Picked<T extends object, S extends SelectorInput<T>> = ExtractSelectorKeys<T, S>;
|
|
35
|
+
/**
|
|
36
|
+
* React hook that subscribes to specific keys in a store with fine-grained re-renders.
|
|
37
|
+
* Only re-renders when the selected keys actually change (using Object.is comparison).
|
|
38
|
+
*
|
|
39
|
+
* @param store - The store created with createStoreState
|
|
40
|
+
* @param selector - Array of keys to subscribe to, or objects with custom compare functions
|
|
41
|
+
* @returns Selected state values from the store
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Subscribe to specific keys
|
|
46
|
+
* const { count, name } = useStoreSelector(store, ['count', 'name']);
|
|
47
|
+
*
|
|
48
|
+
* // Custom comparison for complex objects
|
|
49
|
+
* const { tasks } = useStoreSelector(store, [
|
|
50
|
+
* { tasks: (prev, next) => prev.length === next.length }
|
|
51
|
+
* ]);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
20
54
|
declare function useStoreSelector<T extends object, S extends SelectorInput<T>>(store: StoreType<T>, selector: S): Picked<T, S>;
|
|
55
|
+
/**
|
|
56
|
+
* Interface for storage objects compatible with persistence middleware.
|
|
57
|
+
* Includes localStorage, sessionStorage, AsyncStorage, or any custom storage.
|
|
58
|
+
*/
|
|
59
|
+
interface StorageSupportingInterface {
|
|
60
|
+
getItem(key: string): string | null;
|
|
61
|
+
setItem(key: string, value: string): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a persistence middleware that saves individual keys to storage.
|
|
65
|
+
* Only writes when the specified keys actually change, using per-key storage.
|
|
66
|
+
* Storage format: `${persistKey}:${keyName}` for each persisted key.
|
|
67
|
+
*
|
|
68
|
+
* @param storage - Storage interface (localStorage, sessionStorage, AsyncStorage, etc.)
|
|
69
|
+
* @param persistKey - Base key prefix for storage (e.g., 'myapp' creates 'myapp:theme')
|
|
70
|
+
* @param keys - Array of state keys to persist
|
|
71
|
+
* @returns Tuple of [middleware function, affected keys] for use with addMiddleware
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // Add persistence for theme and user settings
|
|
76
|
+
* store.addMiddleware(
|
|
77
|
+
* createPersistenceMiddleware(localStorage, 'myapp', ['theme', 'isLoggedIn'])
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function createPersistenceMiddleware<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): [MiddlewareFunction<T>, (keyof T)[]];
|
|
82
|
+
/**
|
|
83
|
+
* Loads persisted state from individual key storage during store initialization.
|
|
84
|
+
* Reads keys saved by createPersistenceMiddleware and returns them as partial state.
|
|
85
|
+
*
|
|
86
|
+
* @param storage - Storage interface to read from (same as used in middleware)
|
|
87
|
+
* @param persistKey - Base key prefix used for storage (same as used in middleware)
|
|
88
|
+
* @param keys - Array of keys to restore (should match middleware keys)
|
|
89
|
+
* @returns Partial state object with persisted values, or empty object if loading fails
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* // Load persisted state before creating store
|
|
94
|
+
* const persistedState = loadPersistedState(localStorage, 'myapp', ['theme', 'isLoggedIn']);
|
|
95
|
+
*
|
|
96
|
+
* const store = createStoreState({
|
|
97
|
+
* theme: 'light',
|
|
98
|
+
* isLoggedIn: false,
|
|
99
|
+
* ...persistedState // Apply persisted values
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function loadPersistedState<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): Partial<T>;
|
|
21
104
|
|
|
22
|
-
export { createStoreState, useStoreSelector };
|
|
105
|
+
export { type StorageSupportingInterface, createPersistenceMiddleware, createStoreState, loadPersistedState, useStoreSelector };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
type StoreListener = () => void;
|
|
2
|
+
type MiddlewareFunction<T extends object> = (currentState: T, update: Partial<T>, next: (modifiedUpdate?: Partial<T>) => void) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Creates a new reactive store with fine-grained subscriptions and middleware support.
|
|
5
|
+
*
|
|
6
|
+
* @param initialState - The initial state object for the store
|
|
7
|
+
* @returns Store object with methods: get, set, subscribe, select, addMiddleware
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const store = createStoreState({ count: 0, name: 'John' });
|
|
12
|
+
* store.set({ count: 1 }); // Update state
|
|
13
|
+
* const { count } = useStoreSelector(store, ['count']); // Subscribe in React
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
2
16
|
declare function createStoreState<T extends object>(initialState: T): {
|
|
3
17
|
get: () => T;
|
|
4
|
-
set: (
|
|
18
|
+
set: (update: Partial<T>, debounceDelay?: number | boolean) => void;
|
|
5
19
|
subscribe: (keys: (keyof T)[], listener: StoreListener) => (() => void);
|
|
6
20
|
select: <K extends keyof T>(keys: K[]) => Pick<T, K>;
|
|
21
|
+
addMiddleware: (callbackOrTuple: MiddlewareFunction<T> | [MiddlewareFunction<T>, (keyof T)[]], affectedKeys?: (keyof T)[] | null) => () => void;
|
|
7
22
|
};
|
|
8
23
|
type StoreType<T extends object> = ReturnType<typeof createStoreState<T>>;
|
|
9
24
|
type PrimitiveKey<T extends object> = keyof T;
|
|
@@ -17,6 +32,74 @@ type ExtractSelectorKeys<T extends object, S extends SelectorInput<T>> = {
|
|
|
17
32
|
[K in S[number] extends infer Item ? Item extends keyof T ? Item : keyof Item : never]: T[K];
|
|
18
33
|
};
|
|
19
34
|
type Picked<T extends object, S extends SelectorInput<T>> = ExtractSelectorKeys<T, S>;
|
|
35
|
+
/**
|
|
36
|
+
* React hook that subscribes to specific keys in a store with fine-grained re-renders.
|
|
37
|
+
* Only re-renders when the selected keys actually change (using Object.is comparison).
|
|
38
|
+
*
|
|
39
|
+
* @param store - The store created with createStoreState
|
|
40
|
+
* @param selector - Array of keys to subscribe to, or objects with custom compare functions
|
|
41
|
+
* @returns Selected state values from the store
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Subscribe to specific keys
|
|
46
|
+
* const { count, name } = useStoreSelector(store, ['count', 'name']);
|
|
47
|
+
*
|
|
48
|
+
* // Custom comparison for complex objects
|
|
49
|
+
* const { tasks } = useStoreSelector(store, [
|
|
50
|
+
* { tasks: (prev, next) => prev.length === next.length }
|
|
51
|
+
* ]);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
20
54
|
declare function useStoreSelector<T extends object, S extends SelectorInput<T>>(store: StoreType<T>, selector: S): Picked<T, S>;
|
|
55
|
+
/**
|
|
56
|
+
* Interface for storage objects compatible with persistence middleware.
|
|
57
|
+
* Includes localStorage, sessionStorage, AsyncStorage, or any custom storage.
|
|
58
|
+
*/
|
|
59
|
+
interface StorageSupportingInterface {
|
|
60
|
+
getItem(key: string): string | null;
|
|
61
|
+
setItem(key: string, value: string): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a persistence middleware that saves individual keys to storage.
|
|
65
|
+
* Only writes when the specified keys actually change, using per-key storage.
|
|
66
|
+
* Storage format: `${persistKey}:${keyName}` for each persisted key.
|
|
67
|
+
*
|
|
68
|
+
* @param storage - Storage interface (localStorage, sessionStorage, AsyncStorage, etc.)
|
|
69
|
+
* @param persistKey - Base key prefix for storage (e.g., 'myapp' creates 'myapp:theme')
|
|
70
|
+
* @param keys - Array of state keys to persist
|
|
71
|
+
* @returns Tuple of [middleware function, affected keys] for use with addMiddleware
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // Add persistence for theme and user settings
|
|
76
|
+
* store.addMiddleware(
|
|
77
|
+
* createPersistenceMiddleware(localStorage, 'myapp', ['theme', 'isLoggedIn'])
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function createPersistenceMiddleware<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): [MiddlewareFunction<T>, (keyof T)[]];
|
|
82
|
+
/**
|
|
83
|
+
* Loads persisted state from individual key storage during store initialization.
|
|
84
|
+
* Reads keys saved by createPersistenceMiddleware and returns them as partial state.
|
|
85
|
+
*
|
|
86
|
+
* @param storage - Storage interface to read from (same as used in middleware)
|
|
87
|
+
* @param persistKey - Base key prefix used for storage (same as used in middleware)
|
|
88
|
+
* @param keys - Array of keys to restore (should match middleware keys)
|
|
89
|
+
* @returns Partial state object with persisted values, or empty object if loading fails
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* // Load persisted state before creating store
|
|
94
|
+
* const persistedState = loadPersistedState(localStorage, 'myapp', ['theme', 'isLoggedIn']);
|
|
95
|
+
*
|
|
96
|
+
* const store = createStoreState({
|
|
97
|
+
* theme: 'light',
|
|
98
|
+
* isLoggedIn: false,
|
|
99
|
+
* ...persistedState // Apply persisted values
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function loadPersistedState<T extends object>(storage: Storage | StorageSupportingInterface, persistKey: string, keys: (keyof T)[]): Partial<T>;
|
|
21
104
|
|
|
22
|
-
export { createStoreState, useStoreSelector };
|
|
105
|
+
export { type StorageSupportingInterface, createPersistenceMiddleware, createStoreState, loadPersistedState, useStoreSelector };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var w=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var K=(n,e)=>{for(var t in e)w(n,t,{get:e[t],enumerable:!0})},j=(n,e,t,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of I(e))!F.call(n,i)&&i!==t&&w(n,i,{get:()=>e[i],enumerable:!(a=v(e,i))||a.enumerable});return n};var M=n=>j(w({},"__esModule",{value:!0}),n);var $={};K($,{createPersistenceMiddleware:()=>E,createStoreState:()=>R,loadPersistedState:()=>N,useStoreSelector:()=>z});module.exports=M($);var S=require("react");function C(n,e){let t;return(...a)=>{clearTimeout(t),t=setTimeout(()=>n(...a),e)}}function R(n){let e=n,t=new Map,a=new Map,i=[],k=()=>e,g=(l,f=!1)=>{if(!l)return;let s=l,T=0,r=!1,o=y=>{if(y!==void 0&&(s=y),T>=i.length){r||x(s,f);return}let u=i[T++];if(!u.keys||u.keys.some(c=>c in s)){let c=!1,p=d=>{c||(c=!0,o(d))};try{u.callback(e,s,p)}catch(d){r=!0,console.error("Middleware error:",d);return}if(!c){r=!0;return}}else o()};o()},x=(l,f)=>{var T;let s=[];for(let r in l){let o=r,y=e[o],u=l[o];y!==u&&(Object.is(y,u)||(e[o]=u,s.push(o)))}if(s.length!==0)for(let r of s)f!==!1?(a.has(r)||a.set(r,C(()=>{var o;(o=t.get(r))==null||o.forEach(y=>y())},typeof f=="number"?f:0)),a.get(r)()):(T=t.get(r))==null||T.forEach(o=>o())};return{get:k,set:g,subscribe:(l,f)=>{for(let s of l)t.has(s)||t.set(s,new Set),t.get(s).add(f);return()=>{var s;for(let T of l)(s=t.get(T))==null||s.delete(f)}},select:l=>{let f={},s=e;for(let T of l)f[T]=s[T];return f},addMiddleware:(l,f=null)=>{let s,T;Array.isArray(l)?[s,T]=l:(s=l,T=f);let r={callback:s,keys:T};return i.push(r),()=>{let o=i.indexOf(r);o>-1&&i.splice(o,1)}}}}function V(n,e){return n.length===e.length&&n.every((t,a)=>t===e[a])}function z(n,e){let t=(0,S.useRef)({}),a=(0,S.useRef)(null),i=(0,S.useRef)(null),k=(0,S.useRef)(null),g=(0,S.useRef)(!0),x=(0,S.useRef)({}),m=(0,S.useRef)(null),b=(0,S.useRef)(n),P=b.current!==n;if(P&&(b.current=n,t.current={},a.current=null,i.current=null,k.current=null,g.current=!0,x.current={},m.current=null),!a.current||!V(a.current,e)){let r=[],o=[];for(let y of e)if(typeof y=="string"){let u=y;r.push({key:u}),o.push(u)}else{let u=y;for(let c in u){let p=u[c],d=c;r.push({key:d,compare:p}),o.push(d)}}i.current=r,k.current=o,a.current=e,m.current=null}let l=i.current,f=k.current,s=()=>{let r=n.get();if(g.current){g.current=!1;let c={};for(let{key:p}of l){let d=r[p];x.current[p]=d,c[p]=d}return t.current=c,c}if(!(()=>{for(let{key:c,compare:p}of l){let d=x.current[c],h=r[c];if(d===void 0||(p?!p(d,h):!Object.is(d,h)))return!0}return!1})())return t.current;let u={};for(let{key:c,compare:p}of l){let d=x.current[c],h=r[c];d===void 0||(p?!p(d,h):!Object.is(d,h))?(x.current[c]=h,u[c]=h):u[c]=d}return t.current=u,u},T=(0,S.useMemo)(()=>{let r=n.get(),o={};for(let y of f)o[y]=r[y];return o},[f]);return(!m.current||P)&&(m.current=r=>n.subscribe(f,r)),(0,S.useSyncExternalStore)(m.current,s,()=>T)}function E(n,e,t){return[(i,k,g)=>{let x=t.filter(m=>m in k);if(x.length===0)return g();for(let m of x)try{let b=k[m],P=`${e}:${String(m)}`;n.setItem(P,JSON.stringify(b))}catch(b){console.warn(`Failed to persist key ${String(m)}:`,b)}g()},t]}function N(n,e,t){let a={};for(let i of t)try{let k=`${e}:${String(i)}`,g=n.getItem(k);g!==null&&(a[i]=JSON.parse(g))}catch(k){console.warn(`Failed to load persisted key ${String(i)}:`,k)}return a}0&&(module.exports={createPersistenceMiddleware,createStoreState,loadPersistedState,useStoreSelector});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useMemo as
|
|
1
|
+
import{useMemo as w,useRef as x,useSyncExternalStore as v}from"react";function I(l,s){let n;return(...d)=>{clearTimeout(n),n=setTimeout(()=>l(...d),s)}}function C(l){let s=l,n=new Map,d=new Map,y=[],S=()=>s,m=(c,u=!1)=>{if(!c)return;let r=c,f=0,e=!1,t=T=>{if(T!==void 0&&(r=T),f>=y.length){e||g(r,u);return}let i=y[f++];if(!i.keys||i.keys.some(o=>o in r)){let o=!1,p=a=>{o||(o=!0,t(a))};try{i.callback(s,r,p)}catch(a){e=!0,console.error("Middleware error:",a);return}if(!o){e=!0;return}}else t()};t()},g=(c,u)=>{var f;let r=[];for(let e in c){let t=e,T=s[t],i=c[t];T!==i&&(Object.is(T,i)||(s[t]=i,r.push(t)))}if(r.length!==0)for(let e of r)u!==!1?(d.has(e)||d.set(e,I(()=>{var t;(t=n.get(e))==null||t.forEach(T=>T())},typeof u=="number"?u:0)),d.get(e)()):(f=n.get(e))==null||f.forEach(t=>t())};return{get:S,set:m,subscribe:(c,u)=>{for(let r of c)n.has(r)||n.set(r,new Set),n.get(r).add(u);return()=>{var r;for(let f of c)(r=n.get(f))==null||r.delete(u)}},select:c=>{let u={},r=s;for(let f of c)u[f]=r[f];return u},addMiddleware:(c,u=null)=>{let r,f;Array.isArray(c)?[r,f]=c:(r=c,f=u);let e={callback:r,keys:f};return y.push(e),()=>{let t=y.indexOf(e);t>-1&&y.splice(t,1)}}}}function F(l,s){return l.length===s.length&&l.every((n,d)=>n===s[d])}function R(l,s){let n=x({}),d=x(null),y=x(null),S=x(null),m=x(!0),g=x({}),k=x(null),b=x(l),P=b.current!==l;if(P&&(b.current=l,n.current={},d.current=null,y.current=null,S.current=null,m.current=!0,g.current={},k.current=null),!d.current||!F(d.current,s)){let e=[],t=[];for(let T of s)if(typeof T=="string"){let i=T;e.push({key:i}),t.push(i)}else{let i=T;for(let o in i){let p=i[o],a=o;e.push({key:a,compare:p}),t.push(a)}}y.current=e,S.current=t,d.current=s,k.current=null}let c=y.current,u=S.current,r=()=>{let e=l.get();if(m.current){m.current=!1;let o={};for(let{key:p}of c){let a=e[p];g.current[p]=a,o[p]=a}return n.current=o,o}if(!(()=>{for(let{key:o,compare:p}of c){let a=g.current[o],h=e[o];if(a===void 0||(p?!p(a,h):!Object.is(a,h)))return!0}return!1})())return n.current;let i={};for(let{key:o,compare:p}of c){let a=g.current[o],h=e[o];a===void 0||(p?!p(a,h):!Object.is(a,h))?(g.current[o]=h,i[o]=h):i[o]=a}return n.current=i,i},f=w(()=>{let e=l.get(),t={};for(let T of u)t[T]=e[T];return t},[u]);return(!k.current||P)&&(k.current=e=>l.subscribe(u,e)),v(k.current,r,()=>f)}function V(l,s,n){return[(y,S,m)=>{let g=n.filter(k=>k in S);if(g.length===0)return m();for(let k of g)try{let b=S[k],P=`${s}:${String(k)}`;l.setItem(P,JSON.stringify(b))}catch(b){console.warn(`Failed to persist key ${String(k)}:`,b)}m()},n]}function z(l,s,n){let d={};for(let y of n)try{let S=`${s}:${String(y)}`,m=l.getItem(S);m!==null&&(d[y]=JSON.parse(m))}catch(S){console.warn(`Failed to load persisted key ${String(y)}:`,S)}return d}export{V as createPersistenceMiddleware,C as createStoreState,z as loadPersistedState,R as useStoreSelector};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# React + TypeScript + Vite
|
|
2
|
+
|
|
3
|
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
|
4
|
+
|
|
5
|
+
Currently, two official plugins are available:
|
|
6
|
+
|
|
7
|
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
|
8
|
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
9
|
+
|
|
10
|
+
## Expanding the ESLint configuration
|
|
11
|
+
|
|
12
|
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
export default tseslint.config({
|
|
16
|
+
extends: [
|
|
17
|
+
// Remove ...tseslint.configs.recommended and replace with this
|
|
18
|
+
...tseslint.configs.recommendedTypeChecked,
|
|
19
|
+
// Alternatively, use this for stricter rules
|
|
20
|
+
...tseslint.configs.strictTypeChecked,
|
|
21
|
+
// Optionally, add this for stylistic rules
|
|
22
|
+
...tseslint.configs.stylisticTypeChecked,
|
|
23
|
+
],
|
|
24
|
+
languageOptions: {
|
|
25
|
+
// other options...
|
|
26
|
+
parserOptions: {
|
|
27
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
28
|
+
tsconfigRootDir: import.meta.dirname,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// eslint.config.js
|
|
38
|
+
import reactX from 'eslint-plugin-react-x'
|
|
39
|
+
import reactDom from 'eslint-plugin-react-dom'
|
|
40
|
+
|
|
41
|
+
export default tseslint.config({
|
|
42
|
+
plugins: {
|
|
43
|
+
// Add the react-x and react-dom plugins
|
|
44
|
+
'react-x': reactX,
|
|
45
|
+
'react-dom': reactDom,
|
|
46
|
+
},
|
|
47
|
+
rules: {
|
|
48
|
+
// other rules...
|
|
49
|
+
// Enable its recommended typescript rules
|
|
50
|
+
...reactX.configs['recommended-typescript'].rules,
|
|
51
|
+
...reactDom.configs.recommended.rules,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import tseslint from 'typescript-eslint'
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{ ignores: ['dist'] },
|
|
9
|
+
{
|
|
10
|
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
ecmaVersion: 2020,
|
|
14
|
+
globals: globals.browser,
|
|
15
|
+
},
|
|
16
|
+
plugins: {
|
|
17
|
+
'react-hooks': reactHooks,
|
|
18
|
+
'react-refresh': reactRefresh,
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
...reactHooks.configs.recommended.rules,
|
|
22
|
+
'react-refresh/only-export-components': [
|
|
23
|
+
'warn',
|
|
24
|
+
{ allowConstantExport: true },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Vite + React + TS</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|