@pitboxdev/dynamic-store-zustand 0.0.1 → 0.0.3
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 +121 -172
- package/dist/index.d.mts +17 -46
- package/dist/index.d.ts +17 -46
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,47 +1,84 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# @pitboxdev/dynamic-store-zustand
|
|
2
4
|
|
|
3
5
|
> Two complementary approaches to scalable, type-safe state management in React — both built on top of [Zustand](https://github.com/pmndrs/zustand).
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@pitboxdev/dynamic-store-zustand">
|
|
9
|
+
<img src="https://img.shields.io/npm/v/@pitboxdev/dynamic-store-zustand?style=flat-square" alt="NPM Version" />
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://www.npmjs.com/package/@pitboxdev/dynamic-store-zustand">
|
|
12
|
+
<img src="https://img.shields.io/npm/dw/@pitboxdev/dynamic-store-zustand?style=flat-square" alt="NPM Downloads" />
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://bundlephobia.com/package/@pitboxdev/dynamic-store-zustand">
|
|
15
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@pitboxdev/dynamic-store-zustand?style=flat-square&label=minzipped" alt="Bundle Size" />
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.typescriptlang.org/">
|
|
18
|
+
<img src="https://img.shields.io/badge/TypeScript-Strict-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript Strict" />
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://www.npmjs.com/package/@pitboxdev/dynamic-store-zustand">
|
|
21
|
+
<img src="https://img.shields.io/npm/l/@pitboxdev/dynamic-store-zustand?style=flat-square" alt="License MIT" />
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## ⚡ Features
|
|
30
|
+
|
|
31
|
+
- 🛠️ **DX First (Zero Boilerplate):** Extremely simple API without boilerplate, keeping the standard React `useState` ergonomics.
|
|
32
|
+
- 🧹 **Auto-Cleanup:** Built-in hooks for automatic store resets on unmount.
|
|
33
|
+
- ⚡ **High Performance:** Subscription-free hooks available (`useDynamicStoreMethods`) to read and update state without component re-renders.
|
|
34
|
+
- 🔄 **Functional Updaters:** Handles race conditions naturally with `setData(prev => ...)`.
|
|
35
|
+
- 🛡️ **100% Type-safe:** Written in TypeScript with pristine type inference and autocomplete out of the box.
|
|
36
|
+
- 🪶 **Tiny Footprint:** Minimal addition to your bundle size.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## ❓ Motivation
|
|
41
|
+
|
|
42
|
+
Modern frontend applications often suffer from state management boilerplate and bloated global stores. Setting up stores usually requires defining schemas upfront, creating actions, and wiring things together.
|
|
43
|
+
|
|
44
|
+
The main motivation behind this package is to **drastically reduce boilerplate and simplify store management for maximum efficiency**.
|
|
45
|
+
|
|
46
|
+
Whether you need to generate stores dynamically on the fly or just want the simplicity of `useState` with the power of a scalable global store, this API delivers a seamless, hassle-free experience.
|
|
8
47
|
|
|
9
48
|
---
|
|
10
49
|
|
|
11
50
|
## Table of Contents
|
|
12
51
|
|
|
52
|
+
- [⚡ Features](#-features)
|
|
53
|
+
- [❓ Motivation](#-motivation)
|
|
13
54
|
- [Overview](#overview)
|
|
14
55
|
- [Installation](#installation)
|
|
15
|
-
- [
|
|
56
|
+
- [`useDynamicStore`](#usedynamicstore)
|
|
16
57
|
- [Quick Start](#quick-start)
|
|
17
|
-
- [Outside React](#outside-react)
|
|
18
|
-
- [TypeScript](#typescript)
|
|
19
|
-
- [API 2 — `useDynamicStore`](#api-2--usedynamicstore)
|
|
20
|
-
- [Quick Start](#quick-start-1)
|
|
21
58
|
- [Functional updater (`setData`)](#functional-updater-setdata)
|
|
22
|
-
- [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- [
|
|
59
|
+
- [Examples](#examples)
|
|
60
|
+
- [`useDynamicStoreWithCleanup`](#usedynamicstorewithcleanup)
|
|
61
|
+
- [`useDynamicStoreMethods` (No Subscription)](#usedynamicstoremethods-no-subscription)
|
|
62
|
+
- [Imperative helpers (outside React)](#imperative-helpers-outside-react)
|
|
63
|
+
- [Config options](#config-options)
|
|
64
|
+
- [TypeScript](#typescript)
|
|
27
65
|
- [Full API Reference](#full-api-reference)
|
|
66
|
+
- [Contributing](#contributing)
|
|
28
67
|
- [License](#license)
|
|
29
68
|
|
|
30
69
|
---
|
|
31
70
|
|
|
32
71
|
## Overview
|
|
33
72
|
|
|
34
|
-
|
|
73
|
+
`@pitboxdev/dynamic-store-zustand` provides dynamic store management for React applications. It is built on top of [Zustand](https://github.com/pmndrs/zustand) and exposes a hook API that feels exactly like React's `useState`, with the addition that stores are stored globally in a single shared registry keyed by a string `storeId`.
|
|
35
74
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
| Navigation persistence | — | `persistOnNavigation` config flag |
|
|
44
|
-
| Best for | Permanent, feature-level stores | Page/form/modal scoped state |
|
|
75
|
+
| Feature | Description |
|
|
76
|
+
|---|---|
|
|
77
|
+
| **Dynamic initialization** | Stores are initialized just-in-time when the hook mounts |
|
|
78
|
+
| **useState-like API** | `setData(obj)` or `setData((prev) => update)` |
|
|
79
|
+
| **Auto-cleanup** | `useDynamicStoreWithCleanup` resets state on unmount |
|
|
80
|
+
| **Navigation reset** | Non-persistent stores reset via imperative API on route changes |
|
|
81
|
+
| **Imperative helpers** | Modify and reset stores outside React components |
|
|
45
82
|
|
|
46
83
|
---
|
|
47
84
|
|
|
@@ -59,99 +96,7 @@ pnpm add @pitboxdev/dynamic-store-zustand zustand
|
|
|
59
96
|
|
|
60
97
|
---
|
|
61
98
|
|
|
62
|
-
##
|
|
63
|
-
|
|
64
|
-
A factory function that creates a standalone, fully typed Zustand store from a configuration object with explicit actions.
|
|
65
|
-
|
|
66
|
-
### Quick Start
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import { createDynamicStore } from "@pitboxdev/dynamic-store-zustand";
|
|
70
|
-
|
|
71
|
-
const { useStore } = createDynamicStore({
|
|
72
|
-
initialState: { count: 0 },
|
|
73
|
-
actions: (set) => ({
|
|
74
|
-
increment: () => set((s) => ({ count: s.count + 1 })),
|
|
75
|
-
decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
76
|
-
reset: () => set({ count: 0 }),
|
|
77
|
-
}),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
function Counter() {
|
|
81
|
-
const count = useStore((s) => s.count);
|
|
82
|
-
const { increment, decrement, reset } = useStore();
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<div>
|
|
86
|
-
<button onClick={decrement}>-</button>
|
|
87
|
-
<span>{count}</span>
|
|
88
|
-
<button onClick={increment}>+</button>
|
|
89
|
-
<button onClick={reset}>Reset</button>
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Outside React
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
const { store } = createDynamicStore({ initialState: { count: 0 }, actions: (set) => ({
|
|
99
|
-
increment: () => set((s) => ({ count: s.count + 1 })),
|
|
100
|
-
}) });
|
|
101
|
-
|
|
102
|
-
// Read
|
|
103
|
-
const count = store.getState().count;
|
|
104
|
-
|
|
105
|
-
// Write
|
|
106
|
-
store.setState({ count: 42 });
|
|
107
|
-
|
|
108
|
-
// Subscribe
|
|
109
|
-
const unsub = store.subscribe((state) => console.log(state.count));
|
|
110
|
-
unsub();
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### TypeScript
|
|
114
|
-
|
|
115
|
-
All types are inferred automatically. You can also define them explicitly:
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
import {
|
|
119
|
-
createDynamicStore,
|
|
120
|
-
type DynamicStoreConfig,
|
|
121
|
-
} from "@pitboxdev/dynamic-store-zustand";
|
|
122
|
-
|
|
123
|
-
interface CounterState { count: number }
|
|
124
|
-
interface CounterActions {
|
|
125
|
-
increment: () => void;
|
|
126
|
-
decrement: () => void;
|
|
127
|
-
reset: () => void;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const config: DynamicStoreConfig<CounterState, CounterActions> = {
|
|
131
|
-
initialState: { count: 0 },
|
|
132
|
-
actions: (set) => ({
|
|
133
|
-
increment: () => set((s) => ({ count: s.count + 1 })),
|
|
134
|
-
decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
135
|
-
reset: () => set({ count: 0 }),
|
|
136
|
-
}),
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const { useStore, store } = createDynamicStore(config);
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Using multiple selectors with shallow equality:
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
import { useShallow } from "zustand/react/shallow";
|
|
146
|
-
|
|
147
|
-
const { count, increment } = useStore(
|
|
148
|
-
useShallow((s) => ({ count: s.count, increment: s.increment }))
|
|
149
|
-
);
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## API 2 — `useDynamicStore`
|
|
99
|
+
## `useDynamicStore`
|
|
155
100
|
|
|
156
101
|
A hook that stores state in a single shared registry keyed by a string `storeId`. `setData` works exactly like React's `useState` setter — it accepts either a partial object or a function that receives the previous state.
|
|
157
102
|
|
|
@@ -220,6 +165,8 @@ setData((prev) => ({ value: prev.value + 1 }));
|
|
|
220
165
|
|
|
221
166
|
Always prefer the functional form when the new state depends on the old state.
|
|
222
167
|
|
|
168
|
+
### Examples
|
|
169
|
+
|
|
223
170
|
#### Todo list example
|
|
224
171
|
|
|
225
172
|
```tsx
|
|
@@ -290,7 +237,9 @@ function Cart() {
|
|
|
290
237
|
}
|
|
291
238
|
```
|
|
292
239
|
|
|
293
|
-
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## `useDynamicStoreWithCleanup`
|
|
294
243
|
|
|
295
244
|
`useDynamicStoreWithCleanup` works identically to `useDynamicStore` but resets the store when the component unmounts — useful for modal dialogs, wizard steps, or edit forms.
|
|
296
245
|
|
|
@@ -307,7 +256,41 @@ function EditModal() {
|
|
|
307
256
|
}
|
|
308
257
|
```
|
|
309
258
|
|
|
310
|
-
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## `useDynamicStoreMethods` (No Subscription)
|
|
262
|
+
|
|
263
|
+
If you need to update or read the store **without subscribing to its changes** (to avoid component re-renders), you can use `useDynamicStoreMethods`:
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { useDynamicStoreMethods } from "@pitboxdev/dynamic-store-zustand";
|
|
267
|
+
|
|
268
|
+
function Controls() {
|
|
269
|
+
// This component will NOT re-render when 'counter' state changes!
|
|
270
|
+
const { setData, reset, get } = useDynamicStoreMethods<CounterState>("counter");
|
|
271
|
+
|
|
272
|
+
const increment = () => {
|
|
273
|
+
// Both forms work just like in the regular hook:
|
|
274
|
+
setData((prev) => ({ value: prev.value + 1 }));
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const logCurrent = () => {
|
|
278
|
+
console.log("Current state:", get()); // Get current state without subscribing
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<div>
|
|
283
|
+
<button onClick={increment}>Increment</button>
|
|
284
|
+
<button onClick={logCurrent}>Log State</button>
|
|
285
|
+
<button onClick={reset}>Reset</button>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Imperative helpers (outside React)
|
|
311
294
|
|
|
312
295
|
All helpers call into the shared manager directly — no hook required.
|
|
313
296
|
|
|
@@ -332,7 +315,9 @@ resetAllDynamicStores();
|
|
|
332
315
|
resetNonPersistentDynamicStores();
|
|
333
316
|
```
|
|
334
317
|
|
|
335
|
-
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Config options
|
|
336
321
|
|
|
337
322
|
| Option | Type | Default | Description |
|
|
338
323
|
|---|---|---|---|
|
|
@@ -340,7 +325,9 @@ resetNonPersistentDynamicStores();
|
|
|
340
325
|
| `persistOnNavigation` | `boolean` | `false` | Skip reset when `resetNonPersistentDynamicStores()` is called |
|
|
341
326
|
| `resetOnUnmount` | `boolean` | `false` | Auto-reset when the component unmounts (`useDynamicStoreWithCleanup` only) |
|
|
342
327
|
|
|
343
|
-
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## TypeScript
|
|
344
331
|
|
|
345
332
|
```ts
|
|
346
333
|
import {
|
|
@@ -383,56 +370,8 @@ function RegistrationForm() {
|
|
|
383
370
|
|
|
384
371
|
---
|
|
385
372
|
|
|
386
|
-
## When to use which API
|
|
387
|
-
|
|
388
|
-
### Use `createDynamicStore` when you need:
|
|
389
|
-
|
|
390
|
-
- A **permanent, feature-level store** (auth, theme, user profile, global UI)
|
|
391
|
-
- **Explicit, named actions** that encapsulate business logic
|
|
392
|
-
- **Fine-grained selectors** and subscriptions outside React
|
|
393
|
-
- The full Zustand API (middleware, persist, devtools, subscribe)
|
|
394
|
-
|
|
395
|
-
```ts
|
|
396
|
-
// auth.store.ts
|
|
397
|
-
export const { useStore: useAuthStore, store: authStore } = createDynamicStore({
|
|
398
|
-
initialState: { user: null as User | null, token: "" },
|
|
399
|
-
actions: (set) => ({
|
|
400
|
-
login: (user: User, token: string) => set({ user, token }),
|
|
401
|
-
logout: () => set({ user: null, token: "" }),
|
|
402
|
-
}),
|
|
403
|
-
});
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### Use `useDynamicStore` when you need:
|
|
407
|
-
|
|
408
|
-
- **Page / route / modal scoped state** with optional auto-cleanup
|
|
409
|
-
- **useState-like ergonomics** without boilerplate action definitions
|
|
410
|
-
- **Multiple stores** managed centrally with shared reset helpers
|
|
411
|
-
- Quick iteration when action interfaces aren't stable yet
|
|
412
|
-
|
|
413
|
-
```tsx
|
|
414
|
-
// Inside a wizard step component
|
|
415
|
-
const { data, setData } = useDynamicStoreWithCleanup<StepState>(
|
|
416
|
-
"wizard-step-2",
|
|
417
|
-
{ initialState: { selection: null }, resetOnUnmount: true }
|
|
418
|
-
);
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
---
|
|
422
|
-
|
|
423
373
|
## Full API Reference
|
|
424
374
|
|
|
425
|
-
### `createDynamicStore(config)`
|
|
426
|
-
|
|
427
|
-
| Parameter | Type | Description |
|
|
428
|
-
|---|---|---|
|
|
429
|
-
| `config.initialState` | `TState` | Plain object representing the initial state |
|
|
430
|
-
| `config.actions` | `(set, get) => TActions` | Factory that returns named action implementations |
|
|
431
|
-
|
|
432
|
-
Returns `{ useStore, store }`.
|
|
433
|
-
|
|
434
|
-
---
|
|
435
|
-
|
|
436
375
|
### `useDynamicStore<T>(storeId, config?)`
|
|
437
376
|
|
|
438
377
|
| Parameter | Type | Description |
|
|
@@ -450,11 +389,18 @@ Same signature as `useDynamicStore`. Calls `reset()` on component unmount when `
|
|
|
450
389
|
|
|
451
390
|
---
|
|
452
391
|
|
|
392
|
+
### `useDynamicStoreMethods<T>(storeId, config?)`
|
|
393
|
+
|
|
394
|
+
Returns `{ setData, reset, get }` bound to the store, without subscribing the component to state changes.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
453
398
|
### Imperative helpers
|
|
454
399
|
|
|
455
400
|
| Function | Signature | Description |
|
|
456
401
|
|---|---|---|
|
|
457
402
|
| `updateDynamicStore` | `(storeId, data) => void` | Merge data into a store from outside React |
|
|
403
|
+
| `getDynamicStoreData` | `(storeId) => T \| undefined` | Retrieve a store's current state from outside React |
|
|
458
404
|
| `resetDynamicStore` | `(storeId) => void` | Reset one store to its `initialState` |
|
|
459
405
|
| `resetAllDynamicStores` | `() => void` | Reset every registered store |
|
|
460
406
|
| `resetNonPersistentDynamicStores` | `() => void` | Reset stores where `persistOnNavigation` is not `true` |
|
|
@@ -466,14 +412,17 @@ Same signature as `useDynamicStore`. Calls `reset()` on component unmount when `
|
|
|
466
412
|
| Type | Description |
|
|
467
413
|
|---|---|
|
|
468
414
|
| `StoreState` | `Record<string, unknown>` — base constraint for state objects |
|
|
469
|
-
| `StoreActions` | `Record<string, (...args) => unknown>` — base constraint for action maps |
|
|
470
|
-
| `DynamicStoreConfig<TState, TActions>` | Config type for `createDynamicStore` |
|
|
471
|
-
| `DynamicStore<TState, TActions>` | Return type of `createDynamicStore` |
|
|
472
|
-
| `StoreSlice<TState, TActions>` | Merged state + actions type |
|
|
473
415
|
| `StoreConfig<T>` | Config type for `useDynamicStore` |
|
|
474
416
|
| `SetStateAction<T>` | `Partial<T> \| ((prev: T) => Partial<T>)` — setter argument type |
|
|
475
417
|
| `DynamicStoreRegistry` | Internal registry entry (advanced use) |
|
|
476
418
|
| `UseDynamicStoreReturn<T>` | Return type of `useDynamicStore` |
|
|
419
|
+
| `UseDynamicStoreMethodsReturn<T>` | Return type of `useDynamicStoreMethods` |
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Contributing
|
|
424
|
+
|
|
425
|
+
Contributions, issues, and feature requests are welcome! Feel free to check the [issues page](https://github.com/pitboxdev/dynamic-store-zustand/issues).
|
|
477
426
|
|
|
478
427
|
---
|
|
479
428
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { UseBoundStore, StoreApi } from 'zustand';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Updater argument for setData — either a partial object or a function
|
|
5
3
|
* that receives the previous state and returns a partial object.
|
|
@@ -30,55 +28,23 @@ interface DynamicStoreRegistry {
|
|
|
30
28
|
* Keys are strings, values can be anything.
|
|
31
29
|
*/
|
|
32
30
|
type StoreState = Record<string, unknown>;
|
|
33
|
-
/**
|
|
34
|
-
* Actions are functions attached to the store.
|
|
35
|
-
*/
|
|
36
|
-
type StoreActions = Record<string, (...args: unknown[]) => unknown>;
|
|
37
|
-
/**
|
|
38
|
-
* Full store slice = state + actions.
|
|
39
|
-
*/
|
|
40
|
-
type StoreSlice<TState extends StoreState = StoreState, TActions extends StoreActions = StoreActions> = TState & TActions;
|
|
41
|
-
/**
|
|
42
|
-
* Configuration object passed to createDynamicStore.
|
|
43
|
-
*/
|
|
44
|
-
interface DynamicStoreConfig<TState extends StoreState, TActions extends StoreActions> {
|
|
45
|
-
/** Initial state values */
|
|
46
|
-
initialState: TState;
|
|
47
|
-
/** Factory that receives `set` and `get` and returns action implementations */
|
|
48
|
-
actions: (set: StoreApi<StoreSlice<TState, TActions>>["setState"], get: StoreApi<StoreSlice<TState, TActions>>["getState"]) => TActions;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* The return type of createDynamicStore.
|
|
52
|
-
*/
|
|
53
|
-
interface DynamicStore<TState extends StoreState, TActions extends StoreActions> {
|
|
54
|
-
/** Zustand React hook */
|
|
55
|
-
useStore: UseBoundStore<StoreApi<StoreSlice<TState, TActions>>>;
|
|
56
|
-
/** Direct access to the underlying Zustand store API */
|
|
57
|
-
store: StoreApi<StoreSlice<TState, TActions>>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Creates a dynamic Zustand store from a configuration object.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```ts
|
|
65
|
-
* const { useStore } = createDynamicStore({
|
|
66
|
-
* initialState: { count: 0 },
|
|
67
|
-
* actions: (set) => ({
|
|
68
|
-
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
69
|
-
* decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
70
|
-
* reset: () => set({ count: 0 }),
|
|
71
|
-
* }),
|
|
72
|
-
* });
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
declare function createDynamicStore<TState extends StoreState, TActions extends StoreActions>(config: DynamicStoreConfig<TState, TActions>): DynamicStore<TState, TActions>;
|
|
76
31
|
|
|
32
|
+
interface UseDynamicStoreMethodsReturn<T extends StoreState> {
|
|
33
|
+
setData: (updater: SetStateAction<T>) => void;
|
|
34
|
+
reset: () => void;
|
|
35
|
+
get: () => T;
|
|
36
|
+
}
|
|
77
37
|
interface UseDynamicStoreReturn<T extends StoreState> {
|
|
78
38
|
data: T;
|
|
79
39
|
setData: (updater: SetStateAction<T>) => void;
|
|
80
40
|
reset: () => void;
|
|
81
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Hook that returns store methods (setData, reset, get) WITHOUT subscribing
|
|
44
|
+
* to state changes. Useful when you only need to update or read the store
|
|
45
|
+
* imperatively without causing component re-renders.
|
|
46
|
+
*/
|
|
47
|
+
declare function useDynamicStoreMethods<T extends StoreState>(storeId: string, config?: StoreConfig<T>): UseDynamicStoreMethodsReturn<T>;
|
|
82
48
|
/**
|
|
83
49
|
* Hook-based dynamic store keyed by `storeId`.
|
|
84
50
|
*
|
|
@@ -117,6 +83,11 @@ declare function useDynamicStoreWithCleanup<T extends StoreState>(storeId: strin
|
|
|
117
83
|
* Update a dynamic store from outside a React component.
|
|
118
84
|
*/
|
|
119
85
|
declare const updateDynamicStore: (storeId: string, data: StoreState) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Retrieve the current state of a dynamic store from outside a React component.
|
|
88
|
+
* Fallbacks to empty object if store does not exist.
|
|
89
|
+
*/
|
|
90
|
+
declare const getDynamicStoreData: <T extends StoreState = StoreState>(storeId: string) => T | undefined;
|
|
120
91
|
/**
|
|
121
92
|
* Reset a single dynamic store to its initial state from outside React.
|
|
122
93
|
*/
|
|
@@ -130,4 +101,4 @@ declare const resetAllDynamicStores: () => void;
|
|
|
130
101
|
*/
|
|
131
102
|
declare const resetNonPersistentDynamicStores: () => void;
|
|
132
103
|
|
|
133
|
-
export { type
|
|
104
|
+
export { type DynamicStoreRegistry, type SetStateAction, type StoreConfig, type UseDynamicStoreMethodsReturn, type UseDynamicStoreReturn, getDynamicStoreData, resetAllDynamicStores, resetDynamicStore, resetNonPersistentDynamicStores, updateDynamicStore, useDynamicStore, useDynamicStoreMethods, useDynamicStoreWithCleanup };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { UseBoundStore, StoreApi } from 'zustand';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Updater argument for setData — either a partial object or a function
|
|
5
3
|
* that receives the previous state and returns a partial object.
|
|
@@ -30,55 +28,23 @@ interface DynamicStoreRegistry {
|
|
|
30
28
|
* Keys are strings, values can be anything.
|
|
31
29
|
*/
|
|
32
30
|
type StoreState = Record<string, unknown>;
|
|
33
|
-
/**
|
|
34
|
-
* Actions are functions attached to the store.
|
|
35
|
-
*/
|
|
36
|
-
type StoreActions = Record<string, (...args: unknown[]) => unknown>;
|
|
37
|
-
/**
|
|
38
|
-
* Full store slice = state + actions.
|
|
39
|
-
*/
|
|
40
|
-
type StoreSlice<TState extends StoreState = StoreState, TActions extends StoreActions = StoreActions> = TState & TActions;
|
|
41
|
-
/**
|
|
42
|
-
* Configuration object passed to createDynamicStore.
|
|
43
|
-
*/
|
|
44
|
-
interface DynamicStoreConfig<TState extends StoreState, TActions extends StoreActions> {
|
|
45
|
-
/** Initial state values */
|
|
46
|
-
initialState: TState;
|
|
47
|
-
/** Factory that receives `set` and `get` and returns action implementations */
|
|
48
|
-
actions: (set: StoreApi<StoreSlice<TState, TActions>>["setState"], get: StoreApi<StoreSlice<TState, TActions>>["getState"]) => TActions;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* The return type of createDynamicStore.
|
|
52
|
-
*/
|
|
53
|
-
interface DynamicStore<TState extends StoreState, TActions extends StoreActions> {
|
|
54
|
-
/** Zustand React hook */
|
|
55
|
-
useStore: UseBoundStore<StoreApi<StoreSlice<TState, TActions>>>;
|
|
56
|
-
/** Direct access to the underlying Zustand store API */
|
|
57
|
-
store: StoreApi<StoreSlice<TState, TActions>>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Creates a dynamic Zustand store from a configuration object.
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```ts
|
|
65
|
-
* const { useStore } = createDynamicStore({
|
|
66
|
-
* initialState: { count: 0 },
|
|
67
|
-
* actions: (set) => ({
|
|
68
|
-
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
69
|
-
* decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
70
|
-
* reset: () => set({ count: 0 }),
|
|
71
|
-
* }),
|
|
72
|
-
* });
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
declare function createDynamicStore<TState extends StoreState, TActions extends StoreActions>(config: DynamicStoreConfig<TState, TActions>): DynamicStore<TState, TActions>;
|
|
76
31
|
|
|
32
|
+
interface UseDynamicStoreMethodsReturn<T extends StoreState> {
|
|
33
|
+
setData: (updater: SetStateAction<T>) => void;
|
|
34
|
+
reset: () => void;
|
|
35
|
+
get: () => T;
|
|
36
|
+
}
|
|
77
37
|
interface UseDynamicStoreReturn<T extends StoreState> {
|
|
78
38
|
data: T;
|
|
79
39
|
setData: (updater: SetStateAction<T>) => void;
|
|
80
40
|
reset: () => void;
|
|
81
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Hook that returns store methods (setData, reset, get) WITHOUT subscribing
|
|
44
|
+
* to state changes. Useful when you only need to update or read the store
|
|
45
|
+
* imperatively without causing component re-renders.
|
|
46
|
+
*/
|
|
47
|
+
declare function useDynamicStoreMethods<T extends StoreState>(storeId: string, config?: StoreConfig<T>): UseDynamicStoreMethodsReturn<T>;
|
|
82
48
|
/**
|
|
83
49
|
* Hook-based dynamic store keyed by `storeId`.
|
|
84
50
|
*
|
|
@@ -117,6 +83,11 @@ declare function useDynamicStoreWithCleanup<T extends StoreState>(storeId: strin
|
|
|
117
83
|
* Update a dynamic store from outside a React component.
|
|
118
84
|
*/
|
|
119
85
|
declare const updateDynamicStore: (storeId: string, data: StoreState) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Retrieve the current state of a dynamic store from outside a React component.
|
|
88
|
+
* Fallbacks to empty object if store does not exist.
|
|
89
|
+
*/
|
|
90
|
+
declare const getDynamicStoreData: <T extends StoreState = StoreState>(storeId: string) => T | undefined;
|
|
120
91
|
/**
|
|
121
92
|
* Reset a single dynamic store to its initial state from outside React.
|
|
122
93
|
*/
|
|
@@ -130,4 +101,4 @@ declare const resetAllDynamicStores: () => void;
|
|
|
130
101
|
*/
|
|
131
102
|
declare const resetNonPersistentDynamicStores: () => void;
|
|
132
103
|
|
|
133
|
-
export { type
|
|
104
|
+
export { type DynamicStoreRegistry, type SetStateAction, type StoreConfig, type UseDynamicStoreMethodsReturn, type UseDynamicStoreReturn, getDynamicStoreData, resetAllDynamicStores, resetDynamicStore, resetNonPersistentDynamicStores, updateDynamicStore, useDynamicStore, useDynamicStoreMethods, useDynamicStoreWithCleanup };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var zustand=require('zustand'),middleware=require('zustand/middleware'),react=require('react');
|
|
1
|
+
'use strict';var zustand=require('zustand'),middleware=require('zustand/middleware'),react=require('react');var a=zustand.create()(middleware.devtools(e=>({stores:{},setStoreData:(t,r,o)=>{e(s=>{let i=s.stores[t],n={data:{...i?.data??o?.initialState??{},...r},config:o??i?.config??{},initialState:o?.initialState??i?.initialState??{}};return {stores:{...s.stores,[t]:n}}});},resetStore:t=>{e(r=>{let o=r.stores[t];return o?{stores:{...r.stores,[t]:{...o,data:{...o.initialState}}}}:r});},resetAllStores:()=>{e(t=>{let r={};for(let[o,s]of Object.entries(t.stores))r[o]={...s,data:{...s.initialState}};return {stores:r}});},resetNonPersistentStores:()=>{e(t=>{let r={};for(let[o,s]of Object.entries(t.stores))r[o]=s.config.persistOnNavigation===true?s:{...s,data:{...s.initialState}};return {stores:r}});}}),{name:"DynamicStoresManager"}));function c(e,t){let r=a(n=>n.setStoreData),o=a(n=>n.resetStore),s=()=>a.getState().stores[e]?.data??t?.initialState??{};return {setData:n=>{if(typeof n=="function"){let D=n(s());r(e,D,t);}else r(e,n,t);},reset:()=>{o(e);},get:s}}function d(e,t){let r=a(i=>i.stores[e]),o=c(e,t);return react.useEffect(()=>{!r&&t?.initialState!==void 0&&a.getState().setStoreData(e,{},t);},[e]),{data:r?.data??t?.initialState??{},setData:o.setData,reset:o.reset}}function g(e,t){let{data:r,setData:o,reset:s}=d(e,t);return react.useEffect(()=>()=>{t?.resetOnUnmount===true&&s();},[e,t?.resetOnUnmount]),{data:r,setData:o,reset:s}}var f=(e,t)=>{a.getState().setStoreData(e,t);},p=e=>a.getState().stores[e]?.data,l=e=>{a.getState().resetStore(e);},T=()=>{a.getState().resetAllStores();},x=()=>{a.getState().resetNonPersistentStores();};exports.getDynamicStoreData=p;exports.resetAllDynamicStores=T;exports.resetDynamicStore=l;exports.resetNonPersistentDynamicStores=x;exports.updateDynamicStore=f;exports.useDynamicStore=d;exports.useDynamicStoreMethods=c;exports.useDynamicStoreWithCleanup=g;//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/createDynamicStore.ts","../src/dynamicStore.ts"],"names":["createDynamicStore","config","initialState","actions","store","create","set","get","useDynamicStoresManager","devtools","storeId","data","state","existingStore","entry","next","id","useDynamicStore","storesManager","storeRegistry","useEffect","updater","currentData","updates","useDynamicStoreWithCleanup","setData","reset","updateDynamicStore","resetDynamicStore","resetAllDynamicStores","resetNonPersistentDynamicStores"],"mappings":"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","file":"index.js","sourcesContent":["import { create } from \"zustand\";\nimport type {\n DynamicStore,\n DynamicStoreConfig,\n StoreActions,\n StoreState,\n StoreSlice,\n} from \"./types\";\n\n/**\n * Creates a dynamic Zustand store from a configuration object.\n *\n * @example\n * ```ts\n * const { useStore } = createDynamicStore({\n * initialState: { count: 0 },\n * actions: (set) => ({\n * increment: () => set((s) => ({ count: s.count + 1 })),\n * decrement: () => set((s) => ({ count: s.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n */\nexport function createDynamicStore<\n TState extends StoreState,\n TActions extends StoreActions,\n>(\n config: DynamicStoreConfig<TState, TActions>,\n): DynamicStore<TState, TActions> {\n const { initialState, actions } = config;\n\n const store = create<StoreSlice<TState, TActions>>()((set, get) => ({\n ...initialState,\n ...actions(set, get),\n }));\n\n return {\n useStore: store,\n store,\n };\n}\n","import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useEffect } from \"react\";\nimport type {\n SetStateAction,\n StoreConfig,\n DynamicStoreRegistry,\n StoreState,\n} from \"./types\";\n\n// ─── Internal manager state ───────────────────────────────────────────────────\n\ninterface DynamicStoresState {\n stores: Record<string, DynamicStoreRegistry>;\n setStoreData: (\n storeId: string,\n data: StoreState,\n config?: StoreConfig,\n ) => void;\n resetStore: (storeId: string) => void;\n resetAllStores: () => void;\n resetNonPersistentStores: () => void;\n}\n\nconst useDynamicStoresManager = create<DynamicStoresState>()(\n devtools(\n (set) => ({\n stores: {},\n\n setStoreData: (storeId, data, config) => {\n set((state) => {\n const existingStore = state.stores[storeId];\n const currentData: StoreState =\n existingStore?.data ?? config?.initialState ?? {};\n\n const entry: DynamicStoreRegistry = {\n data: { ...currentData, ...data },\n config: config ?? existingStore?.config ?? {},\n initialState:\n config?.initialState ?? existingStore?.initialState ?? {},\n };\n\n return {\n stores: { ...state.stores, [storeId]: entry },\n };\n });\n },\n\n resetStore: (storeId) => {\n set((state) => {\n const store = state.stores[storeId];\n if (!store) return state;\n\n return {\n stores: {\n ...state.stores,\n [storeId]: { ...store, data: { ...store.initialState } },\n },\n };\n });\n },\n\n resetAllStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] = { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n\n resetNonPersistentStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] =\n store.config.persistOnNavigation === true\n ? store\n : { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n }),\n { name: \"DynamicStoresManager\" },\n ),\n);\n\n// ─── Return types ─────────────────────────────────────────────────────────────\n\nexport interface UseDynamicStoreReturn<T extends StoreState> {\n data: T;\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n}\n\n// ─── useDynamicStore ──────────────────────────────────────────────────────────\n\n/**\n * Hook-based dynamic store keyed by `storeId`.\n *\n * `setData` accepts either a partial object **or** a function that receives\n * the previous state and returns a partial object — exactly like React's\n * `useState` setter.\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStore<CounterState>('counter', {\n * initialState: { value: 0, step: 1 },\n * });\n *\n * // object update\n * setData({ value: 42 });\n *\n * // functional update — safe for rapid successive calls\n * setData((prev) => ({ value: prev.value + prev.step }));\n * ```\n */\nexport function useDynamicStore<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const storesManager = useDynamicStoresManager();\n const storeRegistry = storesManager.stores[storeId];\n\n // Initialize the store entry on first use\n useEffect(() => {\n if (!storeRegistry && config?.initialState !== undefined) {\n storesManager.setStoreData(storeId, {}, config as StoreConfig);\n }\n // Only run on mount / storeId change — intentional dep list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId]);\n\n const data = (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n\n const setData = (updater: SetStateAction<T>): void => {\n if (typeof updater === \"function\") {\n const currentData = (\n storesManager.stores[storeId]?.data ?? config?.initialState ?? {}\n ) as T;\n const updates = updater(currentData);\n storesManager.setStoreData(\n storeId,\n updates as StoreState,\n config as StoreConfig | undefined,\n );\n } else {\n storesManager.setStoreData(\n storeId,\n updater as StoreState,\n config as StoreConfig | undefined,\n );\n }\n };\n\n const reset = (): void => {\n storesManager.resetStore(storeId);\n };\n\n return { data, setData, reset };\n}\n\n// ─── useDynamicStoreWithCleanup ───────────────────────────────────────────────\n\n/**\n * Same as `useDynamicStore` but automatically resets state when the\n * component unmounts (when `config.resetOnUnmount` is `true`).\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStoreWithCleanup<FormState>(\n * 'editForm',\n * { initialState, resetOnUnmount: true },\n * );\n * ```\n */\nexport function useDynamicStoreWithCleanup<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const { data, setData, reset } = useDynamicStore<T>(storeId, config);\n\n useEffect(() => {\n return () => {\n if (config?.resetOnUnmount === true) {\n reset();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId, config?.resetOnUnmount]);\n\n return { data, setData, reset };\n}\n\n// ─── Imperative helpers (outside React) ──────────────────────────────────────\n\n/**\n * Update a dynamic store from outside a React component.\n */\nexport const updateDynamicStore = (\n storeId: string,\n data: StoreState,\n): void => {\n useDynamicStoresManager.getState().setStoreData(storeId, data);\n};\n\n/**\n * Reset a single dynamic store to its initial state from outside React.\n */\nexport const resetDynamicStore = (storeId: string): void => {\n useDynamicStoresManager.getState().resetStore(storeId);\n};\n\n/**\n * Reset all dynamic stores to their initial states from outside React.\n */\nexport const resetAllDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetAllStores();\n};\n\n/**\n * Reset only stores that do not have `persistOnNavigation: true`.\n */\nexport const resetNonPersistentDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetNonPersistentStores();\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/dynamicStore.ts"],"names":["useDynamicStoresManager","create","devtools","set","storeId","data","config","state","existingStore","entry","store","next","id","useDynamicStoreMethods","setStoreData","resetStore","get","updater","updates","useDynamicStore","storeRegistry","methods","useEffect","useDynamicStoreWithCleanup","setData","reset","updateDynamicStore","getDynamicStoreData","resetDynamicStore","resetAllDynamicStores","resetNonPersistentDynamicStores"],"mappings":"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","file":"index.js","sourcesContent":["import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useEffect } from \"react\";\nimport type {\n SetStateAction,\n StoreConfig,\n DynamicStoreRegistry,\n StoreState,\n} from \"./types\";\n\n// ─── Internal manager state ───────────────────────────────────────────────────\n\ninterface DynamicStoresState {\n stores: Record<string, DynamicStoreRegistry>;\n setStoreData: (\n storeId: string,\n data: StoreState,\n config?: StoreConfig,\n ) => void;\n resetStore: (storeId: string) => void;\n resetAllStores: () => void;\n resetNonPersistentStores: () => void;\n}\n\nconst useDynamicStoresManager = create<DynamicStoresState>()(\n devtools(\n (set) => ({\n stores: {},\n\n setStoreData: (storeId, data, config) => {\n set((state) => {\n const existingStore = state.stores[storeId];\n const currentData: StoreState =\n existingStore?.data ?? config?.initialState ?? {};\n\n const entry: DynamicStoreRegistry = {\n data: { ...currentData, ...data },\n config: config ?? existingStore?.config ?? {},\n initialState:\n config?.initialState ?? existingStore?.initialState ?? {},\n };\n\n return {\n stores: { ...state.stores, [storeId]: entry },\n };\n });\n },\n\n resetStore: (storeId) => {\n set((state) => {\n const store = state.stores[storeId];\n if (!store) return state;\n\n return {\n stores: {\n ...state.stores,\n [storeId]: { ...store, data: { ...store.initialState } },\n },\n };\n });\n },\n\n resetAllStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] = { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n\n resetNonPersistentStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] =\n store.config.persistOnNavigation === true\n ? store\n : { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n }),\n { name: \"DynamicStoresManager\" },\n ),\n);\n\n// ─── Return types ─────────────────────────────────────────────────────────────\n\nexport interface UseDynamicStoreMethodsReturn<T extends StoreState> {\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n get: () => T;\n}\n\nexport interface UseDynamicStoreReturn<T extends StoreState> {\n data: T;\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n}\n\n// ─── useDynamicStoreMethods ───────────────────────────────────────────────────\n\n/**\n * Hook that returns store methods (setData, reset, get) WITHOUT subscribing\n * to state changes. Useful when you only need to update or read the store\n * imperatively without causing component re-renders.\n */\nexport function useDynamicStoreMethods<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreMethodsReturn<T> {\n const setStoreData = useDynamicStoresManager((state) => state.setStoreData);\n const resetStore = useDynamicStoresManager((state) => state.resetStore);\n\n const get = (): T => {\n const storeRegistry = useDynamicStoresManager.getState().stores[storeId];\n return (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n };\n\n const setData = (updater: SetStateAction<T>): void => {\n if (typeof updater === \"function\") {\n const updates = updater(get());\n setStoreData(\n storeId,\n updates as StoreState,\n config as StoreConfig | undefined,\n );\n } else {\n setStoreData(\n storeId,\n updater as StoreState,\n config as StoreConfig | undefined,\n );\n }\n };\n\n const reset = (): void => {\n resetStore(storeId);\n };\n\n return { setData, reset, get };\n}\n\n// ─── useDynamicStore ──────────────────────────────────────────────────────────\n\n/**\n * Hook-based dynamic store keyed by `storeId`.\n *\n * `setData` accepts either a partial object **or** a function that receives\n * the previous state and returns a partial object — exactly like React's\n * `useState` setter.\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStore<CounterState>('counter', {\n * initialState: { value: 0, step: 1 },\n * });\n *\n * // object update\n * setData({ value: 42 });\n *\n * // functional update — safe for rapid successive calls\n * setData((prev) => ({ value: prev.value + prev.step }));\n * ```\n */\nexport function useDynamicStore<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const storeRegistry = useDynamicStoresManager((state) => state.stores[storeId]);\n const methods = useDynamicStoreMethods<T>(storeId, config);\n\n // Initialize the store entry on first use\n useEffect(() => {\n if (!storeRegistry && config?.initialState !== undefined) {\n useDynamicStoresManager.getState().setStoreData(\n storeId,\n {},\n config as StoreConfig,\n );\n }\n // Only run on mount / storeId change — intentional dep list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId]);\n\n const data = (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n\n return { data, setData: methods.setData, reset: methods.reset };\n}\n\n// ─── useDynamicStoreWithCleanup ───────────────────────────────────────────────\n\n/**\n * Same as `useDynamicStore` but automatically resets state when the\n * component unmounts (when `config.resetOnUnmount` is `true`).\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStoreWithCleanup<FormState>(\n * 'editForm',\n * { initialState, resetOnUnmount: true },\n * );\n * ```\n */\nexport function useDynamicStoreWithCleanup<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const { data, setData, reset } = useDynamicStore<T>(storeId, config);\n\n useEffect(() => {\n return () => {\n if (config?.resetOnUnmount === true) {\n reset();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId, config?.resetOnUnmount]);\n\n return { data, setData, reset };\n}\n\n// ─── Imperative helpers (outside React) ──────────────────────────────────────\n\n/**\n * Update a dynamic store from outside a React component.\n */\nexport const updateDynamicStore = (\n storeId: string,\n data: StoreState,\n): void => {\n useDynamicStoresManager.getState().setStoreData(storeId, data);\n};\n\n/**\n * Retrieve the current state of a dynamic store from outside a React component.\n * Fallbacks to empty object if store does not exist.\n */\nexport const getDynamicStoreData = <T extends StoreState = StoreState>(\n storeId: string,\n): T | undefined => {\n return useDynamicStoresManager.getState().stores[storeId]?.data as T | undefined;\n};\n\n/**\n * Reset a single dynamic store to its initial state from outside React.\n */\nexport const resetDynamicStore = (storeId: string): void => {\n useDynamicStoresManager.getState().resetStore(storeId);\n};\n\n/**\n * Reset all dynamic stores to their initial states from outside React.\n */\nexport const resetAllDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetAllStores();\n};\n\n/**\n * Reset only stores that do not have `persistOnNavigation: true`.\n */\nexport const resetNonPersistentDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetNonPersistentStores();\n};\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {create}from'zustand';import {devtools}from'zustand/middleware';import {useEffect}from'react';
|
|
1
|
+
import {create}from'zustand';import {devtools}from'zustand/middleware';import {useEffect}from'react';var a=create()(devtools(e=>({stores:{},setStoreData:(t,r,o)=>{e(s=>{let i=s.stores[t],n={data:{...i?.data??o?.initialState??{},...r},config:o??i?.config??{},initialState:o?.initialState??i?.initialState??{}};return {stores:{...s.stores,[t]:n}}});},resetStore:t=>{e(r=>{let o=r.stores[t];return o?{stores:{...r.stores,[t]:{...o,data:{...o.initialState}}}}:r});},resetAllStores:()=>{e(t=>{let r={};for(let[o,s]of Object.entries(t.stores))r[o]={...s,data:{...s.initialState}};return {stores:r}});},resetNonPersistentStores:()=>{e(t=>{let r={};for(let[o,s]of Object.entries(t.stores))r[o]=s.config.persistOnNavigation===true?s:{...s,data:{...s.initialState}};return {stores:r}});}}),{name:"DynamicStoresManager"}));function c(e,t){let r=a(n=>n.setStoreData),o=a(n=>n.resetStore),s=()=>a.getState().stores[e]?.data??t?.initialState??{};return {setData:n=>{if(typeof n=="function"){let D=n(s());r(e,D,t);}else r(e,n,t);},reset:()=>{o(e);},get:s}}function d(e,t){let r=a(i=>i.stores[e]),o=c(e,t);return useEffect(()=>{!r&&t?.initialState!==void 0&&a.getState().setStoreData(e,{},t);},[e]),{data:r?.data??t?.initialState??{},setData:o.setData,reset:o.reset}}function g(e,t){let{data:r,setData:o,reset:s}=d(e,t);return useEffect(()=>()=>{t?.resetOnUnmount===true&&s();},[e,t?.resetOnUnmount]),{data:r,setData:o,reset:s}}var f=(e,t)=>{a.getState().setStoreData(e,t);},p=e=>a.getState().stores[e]?.data,l=e=>{a.getState().resetStore(e);},T=()=>{a.getState().resetAllStores();},x=()=>{a.getState().resetNonPersistentStores();};export{p as getDynamicStoreData,T as resetAllDynamicStores,l as resetDynamicStore,x as resetNonPersistentDynamicStores,f as updateDynamicStore,d as useDynamicStore,c as useDynamicStoreMethods,g as useDynamicStoreWithCleanup};//# sourceMappingURL=index.mjs.map
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/createDynamicStore.ts","../src/dynamicStore.ts"],"names":["createDynamicStore","config","initialState","actions","store","create","set","get","useDynamicStoresManager","devtools","storeId","data","state","existingStore","entry","next","id","useDynamicStore","storesManager","storeRegistry","useEffect","updater","currentData","updates","useDynamicStoreWithCleanup","setData","reset","updateDynamicStore","resetDynamicStore","resetAllDynamicStores","resetNonPersistentDynamicStores"],"mappings":"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","file":"index.mjs","sourcesContent":["import { create } from \"zustand\";\nimport type {\n DynamicStore,\n DynamicStoreConfig,\n StoreActions,\n StoreState,\n StoreSlice,\n} from \"./types\";\n\n/**\n * Creates a dynamic Zustand store from a configuration object.\n *\n * @example\n * ```ts\n * const { useStore } = createDynamicStore({\n * initialState: { count: 0 },\n * actions: (set) => ({\n * increment: () => set((s) => ({ count: s.count + 1 })),\n * decrement: () => set((s) => ({ count: s.count - 1 })),\n * reset: () => set({ count: 0 }),\n * }),\n * });\n * ```\n */\nexport function createDynamicStore<\n TState extends StoreState,\n TActions extends StoreActions,\n>(\n config: DynamicStoreConfig<TState, TActions>,\n): DynamicStore<TState, TActions> {\n const { initialState, actions } = config;\n\n const store = create<StoreSlice<TState, TActions>>()((set, get) => ({\n ...initialState,\n ...actions(set, get),\n }));\n\n return {\n useStore: store,\n store,\n };\n}\n","import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useEffect } from \"react\";\nimport type {\n SetStateAction,\n StoreConfig,\n DynamicStoreRegistry,\n StoreState,\n} from \"./types\";\n\n// ─── Internal manager state ───────────────────────────────────────────────────\n\ninterface DynamicStoresState {\n stores: Record<string, DynamicStoreRegistry>;\n setStoreData: (\n storeId: string,\n data: StoreState,\n config?: StoreConfig,\n ) => void;\n resetStore: (storeId: string) => void;\n resetAllStores: () => void;\n resetNonPersistentStores: () => void;\n}\n\nconst useDynamicStoresManager = create<DynamicStoresState>()(\n devtools(\n (set) => ({\n stores: {},\n\n setStoreData: (storeId, data, config) => {\n set((state) => {\n const existingStore = state.stores[storeId];\n const currentData: StoreState =\n existingStore?.data ?? config?.initialState ?? {};\n\n const entry: DynamicStoreRegistry = {\n data: { ...currentData, ...data },\n config: config ?? existingStore?.config ?? {},\n initialState:\n config?.initialState ?? existingStore?.initialState ?? {},\n };\n\n return {\n stores: { ...state.stores, [storeId]: entry },\n };\n });\n },\n\n resetStore: (storeId) => {\n set((state) => {\n const store = state.stores[storeId];\n if (!store) return state;\n\n return {\n stores: {\n ...state.stores,\n [storeId]: { ...store, data: { ...store.initialState } },\n },\n };\n });\n },\n\n resetAllStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] = { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n\n resetNonPersistentStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] =\n store.config.persistOnNavigation === true\n ? store\n : { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n }),\n { name: \"DynamicStoresManager\" },\n ),\n);\n\n// ─── Return types ─────────────────────────────────────────────────────────────\n\nexport interface UseDynamicStoreReturn<T extends StoreState> {\n data: T;\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n}\n\n// ─── useDynamicStore ──────────────────────────────────────────────────────────\n\n/**\n * Hook-based dynamic store keyed by `storeId`.\n *\n * `setData` accepts either a partial object **or** a function that receives\n * the previous state and returns a partial object — exactly like React's\n * `useState` setter.\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStore<CounterState>('counter', {\n * initialState: { value: 0, step: 1 },\n * });\n *\n * // object update\n * setData({ value: 42 });\n *\n * // functional update — safe for rapid successive calls\n * setData((prev) => ({ value: prev.value + prev.step }));\n * ```\n */\nexport function useDynamicStore<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const storesManager = useDynamicStoresManager();\n const storeRegistry = storesManager.stores[storeId];\n\n // Initialize the store entry on first use\n useEffect(() => {\n if (!storeRegistry && config?.initialState !== undefined) {\n storesManager.setStoreData(storeId, {}, config as StoreConfig);\n }\n // Only run on mount / storeId change — intentional dep list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId]);\n\n const data = (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n\n const setData = (updater: SetStateAction<T>): void => {\n if (typeof updater === \"function\") {\n const currentData = (\n storesManager.stores[storeId]?.data ?? config?.initialState ?? {}\n ) as T;\n const updates = updater(currentData);\n storesManager.setStoreData(\n storeId,\n updates as StoreState,\n config as StoreConfig | undefined,\n );\n } else {\n storesManager.setStoreData(\n storeId,\n updater as StoreState,\n config as StoreConfig | undefined,\n );\n }\n };\n\n const reset = (): void => {\n storesManager.resetStore(storeId);\n };\n\n return { data, setData, reset };\n}\n\n// ─── useDynamicStoreWithCleanup ───────────────────────────────────────────────\n\n/**\n * Same as `useDynamicStore` but automatically resets state when the\n * component unmounts (when `config.resetOnUnmount` is `true`).\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStoreWithCleanup<FormState>(\n * 'editForm',\n * { initialState, resetOnUnmount: true },\n * );\n * ```\n */\nexport function useDynamicStoreWithCleanup<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const { data, setData, reset } = useDynamicStore<T>(storeId, config);\n\n useEffect(() => {\n return () => {\n if (config?.resetOnUnmount === true) {\n reset();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId, config?.resetOnUnmount]);\n\n return { data, setData, reset };\n}\n\n// ─── Imperative helpers (outside React) ──────────────────────────────────────\n\n/**\n * Update a dynamic store from outside a React component.\n */\nexport const updateDynamicStore = (\n storeId: string,\n data: StoreState,\n): void => {\n useDynamicStoresManager.getState().setStoreData(storeId, data);\n};\n\n/**\n * Reset a single dynamic store to its initial state from outside React.\n */\nexport const resetDynamicStore = (storeId: string): void => {\n useDynamicStoresManager.getState().resetStore(storeId);\n};\n\n/**\n * Reset all dynamic stores to their initial states from outside React.\n */\nexport const resetAllDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetAllStores();\n};\n\n/**\n * Reset only stores that do not have `persistOnNavigation: true`.\n */\nexport const resetNonPersistentDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetNonPersistentStores();\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/dynamicStore.ts"],"names":["useDynamicStoresManager","create","devtools","set","storeId","data","config","state","existingStore","entry","store","next","id","useDynamicStoreMethods","setStoreData","resetStore","get","updater","updates","useDynamicStore","storeRegistry","methods","useEffect","useDynamicStoreWithCleanup","setData","reset","updateDynamicStore","getDynamicStoreData","resetDynamicStore","resetAllDynamicStores","resetNonPersistentDynamicStores"],"mappings":"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","file":"index.mjs","sourcesContent":["import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useEffect } from \"react\";\nimport type {\n SetStateAction,\n StoreConfig,\n DynamicStoreRegistry,\n StoreState,\n} from \"./types\";\n\n// ─── Internal manager state ───────────────────────────────────────────────────\n\ninterface DynamicStoresState {\n stores: Record<string, DynamicStoreRegistry>;\n setStoreData: (\n storeId: string,\n data: StoreState,\n config?: StoreConfig,\n ) => void;\n resetStore: (storeId: string) => void;\n resetAllStores: () => void;\n resetNonPersistentStores: () => void;\n}\n\nconst useDynamicStoresManager = create<DynamicStoresState>()(\n devtools(\n (set) => ({\n stores: {},\n\n setStoreData: (storeId, data, config) => {\n set((state) => {\n const existingStore = state.stores[storeId];\n const currentData: StoreState =\n existingStore?.data ?? config?.initialState ?? {};\n\n const entry: DynamicStoreRegistry = {\n data: { ...currentData, ...data },\n config: config ?? existingStore?.config ?? {},\n initialState:\n config?.initialState ?? existingStore?.initialState ?? {},\n };\n\n return {\n stores: { ...state.stores, [storeId]: entry },\n };\n });\n },\n\n resetStore: (storeId) => {\n set((state) => {\n const store = state.stores[storeId];\n if (!store) return state;\n\n return {\n stores: {\n ...state.stores,\n [storeId]: { ...store, data: { ...store.initialState } },\n },\n };\n });\n },\n\n resetAllStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] = { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n\n resetNonPersistentStores: () => {\n set((state) => {\n const next: Record<string, DynamicStoreRegistry> = {};\n\n for (const [id, store] of Object.entries(state.stores)) {\n next[id] =\n store.config.persistOnNavigation === true\n ? store\n : { ...store, data: { ...store.initialState } };\n }\n\n return { stores: next };\n });\n },\n }),\n { name: \"DynamicStoresManager\" },\n ),\n);\n\n// ─── Return types ─────────────────────────────────────────────────────────────\n\nexport interface UseDynamicStoreMethodsReturn<T extends StoreState> {\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n get: () => T;\n}\n\nexport interface UseDynamicStoreReturn<T extends StoreState> {\n data: T;\n setData: (updater: SetStateAction<T>) => void;\n reset: () => void;\n}\n\n// ─── useDynamicStoreMethods ───────────────────────────────────────────────────\n\n/**\n * Hook that returns store methods (setData, reset, get) WITHOUT subscribing\n * to state changes. Useful when you only need to update or read the store\n * imperatively without causing component re-renders.\n */\nexport function useDynamicStoreMethods<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreMethodsReturn<T> {\n const setStoreData = useDynamicStoresManager((state) => state.setStoreData);\n const resetStore = useDynamicStoresManager((state) => state.resetStore);\n\n const get = (): T => {\n const storeRegistry = useDynamicStoresManager.getState().stores[storeId];\n return (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n };\n\n const setData = (updater: SetStateAction<T>): void => {\n if (typeof updater === \"function\") {\n const updates = updater(get());\n setStoreData(\n storeId,\n updates as StoreState,\n config as StoreConfig | undefined,\n );\n } else {\n setStoreData(\n storeId,\n updater as StoreState,\n config as StoreConfig | undefined,\n );\n }\n };\n\n const reset = (): void => {\n resetStore(storeId);\n };\n\n return { setData, reset, get };\n}\n\n// ─── useDynamicStore ──────────────────────────────────────────────────────────\n\n/**\n * Hook-based dynamic store keyed by `storeId`.\n *\n * `setData` accepts either a partial object **or** a function that receives\n * the previous state and returns a partial object — exactly like React's\n * `useState` setter.\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStore<CounterState>('counter', {\n * initialState: { value: 0, step: 1 },\n * });\n *\n * // object update\n * setData({ value: 42 });\n *\n * // functional update — safe for rapid successive calls\n * setData((prev) => ({ value: prev.value + prev.step }));\n * ```\n */\nexport function useDynamicStore<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const storeRegistry = useDynamicStoresManager((state) => state.stores[storeId]);\n const methods = useDynamicStoreMethods<T>(storeId, config);\n\n // Initialize the store entry on first use\n useEffect(() => {\n if (!storeRegistry && config?.initialState !== undefined) {\n useDynamicStoresManager.getState().setStoreData(\n storeId,\n {},\n config as StoreConfig,\n );\n }\n // Only run on mount / storeId change — intentional dep list\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId]);\n\n const data = (storeRegistry?.data ?? config?.initialState ?? {}) as T;\n\n return { data, setData: methods.setData, reset: methods.reset };\n}\n\n// ─── useDynamicStoreWithCleanup ───────────────────────────────────────────────\n\n/**\n * Same as `useDynamicStore` but automatically resets state when the\n * component unmounts (when `config.resetOnUnmount` is `true`).\n *\n * @example\n * ```tsx\n * const { data, setData, reset } = useDynamicStoreWithCleanup<FormState>(\n * 'editForm',\n * { initialState, resetOnUnmount: true },\n * );\n * ```\n */\nexport function useDynamicStoreWithCleanup<T extends StoreState>(\n storeId: string,\n config?: StoreConfig<T>,\n): UseDynamicStoreReturn<T> {\n const { data, setData, reset } = useDynamicStore<T>(storeId, config);\n\n useEffect(() => {\n return () => {\n if (config?.resetOnUnmount === true) {\n reset();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storeId, config?.resetOnUnmount]);\n\n return { data, setData, reset };\n}\n\n// ─── Imperative helpers (outside React) ──────────────────────────────────────\n\n/**\n * Update a dynamic store from outside a React component.\n */\nexport const updateDynamicStore = (\n storeId: string,\n data: StoreState,\n): void => {\n useDynamicStoresManager.getState().setStoreData(storeId, data);\n};\n\n/**\n * Retrieve the current state of a dynamic store from outside a React component.\n * Fallbacks to empty object if store does not exist.\n */\nexport const getDynamicStoreData = <T extends StoreState = StoreState>(\n storeId: string,\n): T | undefined => {\n return useDynamicStoresManager.getState().stores[storeId]?.data as T | undefined;\n};\n\n/**\n * Reset a single dynamic store to its initial state from outside React.\n */\nexport const resetDynamicStore = (storeId: string): void => {\n useDynamicStoresManager.getState().resetStore(storeId);\n};\n\n/**\n * Reset all dynamic stores to their initial states from outside React.\n */\nexport const resetAllDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetAllStores();\n};\n\n/**\n * Reset only stores that do not have `persistOnNavigation: true`.\n */\nexport const resetNonPersistentDynamicStores = (): void => {\n useDynamicStoresManager.getState().resetNonPersistentStores();\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pitboxdev/dynamic-store-zustand",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Dynamic store factory built on top of Zustand for scalable state management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -59,4 +59,4 @@
|
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
61
61
|
}
|
|
62
|
-
}
|
|
62
|
+
}
|