@dvashim/store 1.1.3 → 1.2.1
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 +202 -0
- package/dist/Store.d.ts +25 -4
- package/dist/Store.d.ts.map +1 -1
- package/dist/Store.js +59 -14
- package/dist/Store.js.map +1 -1
- package/dist/createStore.d.ts +1 -1
- package/dist/createStore.d.ts.map +1 -1
- package/dist/createStore.js.map +1 -1
- package/dist/useStore.d.ts +19 -0
- package/dist/useStore.d.ts.map +1 -1
- package/dist/useStore.js +9 -9
- package/dist/useStore.js.map +1 -1
- package/package.json +31 -16
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# @dvashim/store
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@dvashim/store) [](https://www.npmjs.com/package/@dvashim/store) [](https://biomejs.dev)
|
|
4
|
+
|
|
5
|
+
A minimal, lightweight React state management library built on `useSyncExternalStore`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
npm:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @dvashim/store
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
or pnpm:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @dvashim/store
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Peer dependencies:** `react >= 18`
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { createStore, useStore } from '@dvashim/store'
|
|
27
|
+
|
|
28
|
+
const count$ = createStore(0)
|
|
29
|
+
|
|
30
|
+
function Counter() {
|
|
31
|
+
const count = useStore(count$)
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<button onClick={() => count$.update((n) => n + 1)}>
|
|
35
|
+
Count: {count}
|
|
36
|
+
</button>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### `createStore(initialState?)`
|
|
44
|
+
|
|
45
|
+
Creates a new `Store` instance.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const count$ = createStore(0)
|
|
49
|
+
const user$ = createStore({ name: 'Alice', age: 30 })
|
|
50
|
+
|
|
51
|
+
// Without initial state — type defaults to T | undefined
|
|
52
|
+
const data$ = createStore<string>()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `Store`
|
|
56
|
+
|
|
57
|
+
Reactive state container with subscription-based change notification.
|
|
58
|
+
|
|
59
|
+
#### `store.get()`
|
|
60
|
+
|
|
61
|
+
Returns the current state.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const count$ = createStore(10)
|
|
65
|
+
count$.get() // 10
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### `store.set(state, options?)`
|
|
69
|
+
|
|
70
|
+
Replaces the state. Skipped if the value is identical (`Object.is`), unless `{ force: true }` is passed.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
count$.set(5)
|
|
74
|
+
|
|
75
|
+
// Force notify subscribers even if the value hasn't changed
|
|
76
|
+
count$.set(5, { force: true })
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### `store.update(updater, options?)`
|
|
80
|
+
|
|
81
|
+
Derives the next state via an updater function. Re-entrant calls from within a subscriber are queued and flushed in FIFO order.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
count$.update((n) => n + 1)
|
|
85
|
+
|
|
86
|
+
// With objects — always return a new reference
|
|
87
|
+
const todos$ = createStore([{ text: 'Buy milk', done: false }])
|
|
88
|
+
todos$.update((todos) => [...todos, { text: 'Walk dog', done: false }])
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### `store.subscribe(fn)`
|
|
92
|
+
|
|
93
|
+
Registers a callback invoked on each state change. Returns an unsubscribe function.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const unsubscribe = count$.subscribe(() => {
|
|
97
|
+
console.log('Count changed:', count$.get())
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Later...
|
|
101
|
+
unsubscribe()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `useStore(store, selector?)`
|
|
105
|
+
|
|
106
|
+
React hook that subscribes a component to a store.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
function Counter() {
|
|
110
|
+
const count = useStore(count$)
|
|
111
|
+
return <p>{count}</p>
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### With a selector
|
|
116
|
+
|
|
117
|
+
Derive a value from the store state. The selector should return a referentially stable value (primitive or existing object reference) to avoid unnecessary re-renders.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
const user$ = createStore({ name: 'Alice', age: 30 })
|
|
121
|
+
|
|
122
|
+
function UserName() {
|
|
123
|
+
const name = useStore(user$, (user) => user.name)
|
|
124
|
+
return <p>{name}</p>
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Patterns
|
|
129
|
+
|
|
130
|
+
### Shared stores across components
|
|
131
|
+
|
|
132
|
+
Define stores outside of components and import them where needed.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// stores/auth.ts
|
|
136
|
+
import { createStore } from '@dvashim/store'
|
|
137
|
+
|
|
138
|
+
export const token$ = createStore<string | null>(null)
|
|
139
|
+
|
|
140
|
+
export function login(token: string) {
|
|
141
|
+
token$.set(token)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function logout() {
|
|
145
|
+
token$.set(null)
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// components/Profile.tsx
|
|
151
|
+
import { useStore } from '@dvashim/store'
|
|
152
|
+
import { token$, logout } from '../stores/auth'
|
|
153
|
+
|
|
154
|
+
function Profile() {
|
|
155
|
+
const token = useStore(token$)
|
|
156
|
+
|
|
157
|
+
if (!token) return <p>Not logged in</p>
|
|
158
|
+
|
|
159
|
+
return <button onClick={logout}>Log out</button>
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Combining multiple stores
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import { createStore, useStore } from '@dvashim/store'
|
|
167
|
+
|
|
168
|
+
const firstName$ = createStore('Alice')
|
|
169
|
+
const lastName$ = createStore('Smith')
|
|
170
|
+
|
|
171
|
+
function FullName() {
|
|
172
|
+
const firstName = useStore(firstName$)
|
|
173
|
+
const lastName = useStore(lastName$)
|
|
174
|
+
|
|
175
|
+
return <p>{firstName} {lastName}</p>
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Using the Store class directly
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { Store } from '@dvashim/store'
|
|
183
|
+
|
|
184
|
+
class TimerService {
|
|
185
|
+
readonly seconds$ = new Store(0)
|
|
186
|
+
#interval: ReturnType<typeof setInterval> | undefined
|
|
187
|
+
|
|
188
|
+
start() {
|
|
189
|
+
this.#interval = setInterval(() => {
|
|
190
|
+
this.seconds$.update((s) => s + 1)
|
|
191
|
+
}, 1000)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
stop() {
|
|
195
|
+
clearInterval(this.#interval)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/dist/Store.d.ts
CHANGED
|
@@ -3,14 +3,35 @@ type Updater<T> = (prevValue: T) => T;
|
|
|
3
3
|
type UpdateOptions = {
|
|
4
4
|
force?: boolean;
|
|
5
5
|
};
|
|
6
|
+
/**
|
|
7
|
+
* Reactive state container with subscription-based change notification.
|
|
8
|
+
* @typeParam T - The type of the stored state.
|
|
9
|
+
*/
|
|
6
10
|
export declare class Store<T> {
|
|
7
11
|
#private;
|
|
8
12
|
constructor(initialState: T);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Registers a subscriber that is called whenever the state changes.
|
|
15
|
+
* @param fn - Callback invoked on each state change.
|
|
16
|
+
* @returns An unsubscribe function that removes the subscriber.
|
|
17
|
+
*/
|
|
13
18
|
subscribe(fn: Subscriber): () => void;
|
|
19
|
+
/** Returns the current state. */
|
|
20
|
+
get(): T;
|
|
21
|
+
/**
|
|
22
|
+
* Replaces the state and notifies subscribers.
|
|
23
|
+
* Skipped if the value is identical (`Object.is`), unless `force` is set.
|
|
24
|
+
* @param state - The new state value.
|
|
25
|
+
* @param options - Pass `{ force: true }` to notify even when unchanged.
|
|
26
|
+
*/
|
|
27
|
+
set(state: T, options?: UpdateOptions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Derives the next state via an updater function and notifies subscribers.
|
|
30
|
+
* Re-entrant calls from within a subscriber are queued and flushed in FIFO order.
|
|
31
|
+
* @param updater - Receives the current state and returns the next state.
|
|
32
|
+
* @param options - Pass `{ force: true }` to notify even when unchanged.
|
|
33
|
+
*/
|
|
34
|
+
update(updater: Updater<T>, options?: UpdateOptions): void;
|
|
14
35
|
}
|
|
15
36
|
export {};
|
|
16
37
|
//# sourceMappingURL=Store.d.ts.map
|
package/dist/Store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,MAAM,IAAI,CAAA;AAC5B,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;AACrC,KAAK,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"Store.d.ts","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"AAAA,KAAK,UAAU,GAAG,MAAM,IAAI,CAAA;AAC5B,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;AACrC,KAAK,aAAa,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAMxC;;;GAGG;AACH,qBAAa,KAAK,CAAC,CAAC;;gBAMN,YAAY,EAAE,CAAC;IAI3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,IAAI;IAKrC,iCAAiC;IACjC,GAAG,IAAI,CAAC;IAIR;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IAKrC;;;;;OAKG;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;CA0CpD"}
|
package/dist/Store.js
CHANGED
|
@@ -1,37 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive state container with subscription-based change notification.
|
|
3
|
+
* @typeParam T - The type of the stored state.
|
|
4
|
+
*/
|
|
1
5
|
export class Store {
|
|
2
6
|
#subscribers = new Set();
|
|
7
|
+
#queue = [];
|
|
8
|
+
#notifying = false;
|
|
3
9
|
#state;
|
|
4
10
|
constructor(initialState) {
|
|
5
11
|
this.#state = initialState;
|
|
6
12
|
}
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Registers a subscriber that is called whenever the state changes.
|
|
15
|
+
* @param fn - Callback invoked on each state change.
|
|
16
|
+
* @returns An unsubscribe function that removes the subscriber.
|
|
17
|
+
*/
|
|
18
|
+
subscribe(fn) {
|
|
19
|
+
this.#subscribers.add(fn);
|
|
20
|
+
return () => this.#subscribers.delete(fn);
|
|
9
21
|
}
|
|
22
|
+
/** Returns the current state. */
|
|
10
23
|
get() {
|
|
11
24
|
return this.#state;
|
|
12
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Replaces the state and notifies subscribers.
|
|
28
|
+
* Skipped if the value is identical (`Object.is`), unless `force` is set.
|
|
29
|
+
* @param state - The new state value.
|
|
30
|
+
* @param options - Pass `{ force: true }` to notify even when unchanged.
|
|
31
|
+
*/
|
|
13
32
|
set(state, options) {
|
|
14
|
-
|
|
33
|
+
this.#queue.push({ updater: () => state, options });
|
|
34
|
+
return this.#flush();
|
|
15
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Derives the next state via an updater function and notifies subscribers.
|
|
38
|
+
* Re-entrant calls from within a subscriber are queued and flushed in FIFO order.
|
|
39
|
+
* @param updater - Receives the current state and returns the next state.
|
|
40
|
+
* @param options - Pass `{ force: true }` to notify even when unchanged.
|
|
41
|
+
*/
|
|
16
42
|
update(updater, options) {
|
|
17
|
-
|
|
43
|
+
this.#queue.push({ updater, options });
|
|
44
|
+
return this.#flush();
|
|
18
45
|
}
|
|
19
|
-
|
|
20
|
-
this.#
|
|
21
|
-
|
|
46
|
+
#flush() {
|
|
47
|
+
if (this.#notifying) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.#notifying = true;
|
|
51
|
+
try {
|
|
52
|
+
let q = this.#queue.shift();
|
|
53
|
+
while (q) {
|
|
54
|
+
const state = q.updater(this.#state);
|
|
55
|
+
this.#commit(state, q.options);
|
|
56
|
+
q = this.#queue.shift();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.#queue.length = 0;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
this.#notifying = false;
|
|
65
|
+
}
|
|
22
66
|
}
|
|
23
|
-
|
|
24
|
-
* @returns `true` if listeners were notified
|
|
25
|
-
*/
|
|
26
|
-
#notify(nextState, options) {
|
|
67
|
+
#commit(nextState, options) {
|
|
27
68
|
if (!options?.force && Object.is(nextState, this.#state)) {
|
|
28
|
-
return
|
|
69
|
+
return;
|
|
29
70
|
}
|
|
30
71
|
this.#state = nextState;
|
|
31
72
|
for (const listener of [...this.#subscribers]) {
|
|
32
|
-
|
|
73
|
+
try {
|
|
74
|
+
listener();
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('Error in subscriber', error);
|
|
78
|
+
}
|
|
33
79
|
}
|
|
34
|
-
return true;
|
|
35
80
|
}
|
|
36
81
|
}
|
|
37
82
|
//# sourceMappingURL=Store.js.map
|
package/dist/Store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Store.js","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Store.js","sourceRoot":"","sources":["../src/Store.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,OAAO,KAAK;IACP,YAAY,GAAG,IAAI,GAAG,EAAc,CAAA;IACpC,MAAM,GAAmB,EAAE,CAAA;IACpC,UAAU,GAAG,KAAK,CAAA;IAClB,MAAM,CAAG;IAET,YAAY,YAAe;QACzB,IAAI,CAAC,MAAM,GAAG,YAAY,CAAA;IAC5B,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,EAAc;QACtB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACzB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC;IAED,iCAAiC;IACjC,GAAG;QACD,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,KAAQ,EAAE,OAAuB;QACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QACnD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,OAAmB,EAAE,OAAuB;QACjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;QACtC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAA;IACtB,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAM;QACR,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QAEtB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;YAC3B,OAAO,CAAC,EAAE,CAAC;gBACT,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBACpC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;gBAC9B,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAA;YACzB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;YACtB,MAAM,KAAK,CAAA;QACb,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACzB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,SAAY,EAAE,OAAuB;QAC3C,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,OAAM;QACR,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QAEvB,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,QAAQ,EAAE,CAAA;YACZ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
package/dist/createStore.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Store } from './Store';
|
|
2
|
-
declare function createStore<T>(state: T): Store<T>;
|
|
3
2
|
declare function createStore<T = undefined>(): Store<T | undefined>;
|
|
3
|
+
declare function createStore<T>(state: T): Store<T>;
|
|
4
4
|
export { createStore };
|
|
5
5
|
//# sourceMappingURL=createStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.d.ts","sourceRoot":"","sources":["../src/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,iBAAS,WAAW,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"createStore.d.ts","sourceRoot":"","sources":["../src/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,iBAAS,WAAW,CAAC,CAAC,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;AAC3D,iBAAS,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;AAK3C,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
package/dist/createStore.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../src/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAI/B,SAAS,WAAW,CAAI,
|
|
1
|
+
{"version":3,"file":"createStore.js","sourceRoot":"","sources":["../src/createStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAI/B,SAAS,WAAW,CAAI,KAAS;IAC/B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAA;AACzB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
package/dist/useStore.d.ts
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import type { Store } from './Store';
|
|
2
2
|
type Selector<T, U> = (state: T) => U;
|
|
3
|
+
/**
|
|
4
|
+
* Subscribes a React component to a {@link Store} and returns its current state.
|
|
5
|
+
*
|
|
6
|
+
* @param store - The store to subscribe to.
|
|
7
|
+
* @returns The current state of the store.
|
|
8
|
+
*/
|
|
3
9
|
declare function useStore<T>(store: Store<T>): T;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribes a React component to a {@link Store} and returns a derived value
|
|
12
|
+
* computed by the selector.
|
|
13
|
+
*
|
|
14
|
+
* The selector should return a referentially stable value (e.g. a primitive or
|
|
15
|
+
* an existing object reference) to avoid unnecessary re-renders, since snapshots
|
|
16
|
+
* are compared with `Object.is`.
|
|
17
|
+
*
|
|
18
|
+
* @param store - The store to subscribe to.
|
|
19
|
+
* @param selector - A function that derives a value from the store state.
|
|
20
|
+
* @returns The value returned by the selector.
|
|
21
|
+
*/
|
|
4
22
|
declare function useStore<T, U>(store: Store<T>, selector: Selector<T, U>): U;
|
|
5
23
|
export { useStore };
|
|
24
|
+
export type { Selector };
|
|
6
25
|
//# sourceMappingURL=useStore.d.ts.map
|
package/dist/useStore.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStore.d.ts","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;AAErC,iBAAS,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"useStore.d.ts","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,KAAK,QAAQ,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;AAErC;;;;;GAKG;AACH,iBAAS,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAExC;;;;;;;;;;;GAWG;AACH,iBAAS,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;AA0BrE,OAAO,EAAE,QAAQ,EAAE,CAAA;AACnB,YAAY,EAAE,QAAQ,EAAE,CAAA"}
|
package/dist/useStore.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { useDebugValue, useMemo, useSyncExternalStore } from 'react';
|
|
1
|
+
import { useDebugValue, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
2
2
|
function useStore(store, selector) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const selectorRef = useRef(undefined);
|
|
4
|
+
selectorRef.current = selector;
|
|
5
|
+
const args = useMemo(() => {
|
|
6
|
+
const subscribe = store.subscribe.bind(store);
|
|
7
|
+
const getSnapshot = () => selectorRef.current ? selectorRef.current(store.get()) : store.get();
|
|
8
|
+
return { subscribe, getSnapshot };
|
|
9
|
+
}, [store]);
|
|
10
|
+
const value = useSyncExternalStore(args.subscribe, args.getSnapshot, args.getSnapshot);
|
|
11
11
|
useDebugValue(value);
|
|
12
12
|
return value;
|
|
13
13
|
}
|
package/dist/useStore.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStore.js","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"useStore.js","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AA2B5E,SAAS,QAAQ,CAAW,KAAe,EAAE,QAAyB;IACpE,MAAM,WAAW,GAAG,MAAM,CAAkB,SAAS,CAAC,CAAA;IACtD,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAA;IAE9B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;QACxB,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAE7C,MAAM,WAAW,GAAG,GAAG,EAAE,CACvB,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QAEtE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAA;IACnC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;IAEX,MAAM,KAAK,GAAG,oBAAoB,CAChC,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,WAAW,CACjB,CAAA;IAED,aAAa,CAAC,KAAK,CAAC,CAAA;IAEpB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,41 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Yet another state management in React",
|
|
3
|
-
"keywords": [],
|
|
4
3
|
"license": "MIT",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.js",
|
|
7
4
|
"name": "@dvashim/store",
|
|
8
5
|
"type": "module",
|
|
9
6
|
"types": "./dist/index.d.ts",
|
|
10
|
-
"version": "1.1
|
|
7
|
+
"version": "1.2.1",
|
|
11
8
|
"author": {
|
|
12
9
|
"email": "aleksei@dvashim.dev",
|
|
13
10
|
"name": "Aleksei Reznichenko"
|
|
14
11
|
},
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"react": "^19.2.4",
|
|
17
|
-
"react-dom": "^19.2.4"
|
|
18
|
-
},
|
|
19
12
|
"devDependencies": {
|
|
20
|
-
"@biomejs/biome": "^2.4.
|
|
21
|
-
"@changesets/changelog-github": "^0.
|
|
22
|
-
"@changesets/cli": "^2.
|
|
23
|
-
"@dvashim/biome-config": "^1.
|
|
13
|
+
"@biomejs/biome": "^2.4.6",
|
|
14
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
15
|
+
"@changesets/cli": "^2.30.0",
|
|
16
|
+
"@dvashim/biome-config": "^1.3.1",
|
|
24
17
|
"@dvashim/typescript-config": "^1.1.9",
|
|
18
|
+
"@testing-library/react": "^16.3.2",
|
|
19
|
+
"@types/node": "^25.3.5",
|
|
25
20
|
"@types/react": "^19.2.14",
|
|
26
21
|
"@types/react-dom": "^19.2.3",
|
|
22
|
+
"jsdom": "^28.1.0",
|
|
23
|
+
"react": "^19.2.4",
|
|
24
|
+
"react-dom": "^19.2.4",
|
|
27
25
|
"rimraf": "^6.1.3",
|
|
28
|
-
"typescript": "^5.9.3"
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"vitest": "^4.0.18"
|
|
29
28
|
},
|
|
30
29
|
"exports": {
|
|
31
30
|
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
32
|
"import": "./dist/index.js",
|
|
33
|
-
"
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist"
|
|
38
38
|
],
|
|
39
|
+
"keywords": [
|
|
40
|
+
"react",
|
|
41
|
+
"state-manager",
|
|
42
|
+
"useSyncExternalStore",
|
|
43
|
+
"store",
|
|
44
|
+
"minimal",
|
|
45
|
+
"lightweight",
|
|
46
|
+
"typescript"
|
|
47
|
+
],
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
50
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
51
|
+
},
|
|
39
52
|
"publishConfig": {
|
|
40
53
|
"access": "public",
|
|
41
54
|
"provenance": true
|
|
@@ -45,12 +58,14 @@
|
|
|
45
58
|
"url": "https://github.com/dvashim/store.git"
|
|
46
59
|
},
|
|
47
60
|
"scripts": {
|
|
48
|
-
"build": "tsc --
|
|
61
|
+
"build": "tsc --project tsconfig.dev.json",
|
|
49
62
|
"changeset": "changeset",
|
|
50
|
-
"check": "pnpm
|
|
63
|
+
"check": "pnpm run \"/^check:.*/\"",
|
|
51
64
|
"check:biome": "biome check",
|
|
52
65
|
"check:ts": "tsc --noemit",
|
|
53
66
|
"clean": "rm -rf dist *.tsbuildinfo",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
54
69
|
"watch": "pnpm build --watch"
|
|
55
70
|
}
|
|
56
71
|
}
|