dantian 0.0.2-beta.8 → 1.0.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
+
5
+ ### [1.0.2](https://github.com/JuliusKoronciCH/dantian/compare/v1.0.1-beta.0...v1.0.2) (2026-02-08)
6
+
7
+ ### [1.0.1](https://github.com/JuliusKoronciCH/dantian/compare/v1.0.1-beta.0...v1.0.1) (2026-02-08)
8
+
9
+ ### 1.0.1-beta.1 (2026-02-08)
10
+
11
+ ### 1.0.1-beta.0 (2026-02-08)
12
+
13
+ ### 0.0.2-beta.19 (2026-02-08)
14
+
15
+ ### 0.0.2-beta.18 (2024-05-21)
16
+
17
+ ### Bug Fixes
18
+
19
+ - useIsHydrated race condition fix on later call of the hook ([#19](https://github.com/JuliusKoronciCH/dantian/issues/19)) ([2c7c0ff](https://github.com/JuliusKoronciCH/dantian/commit/2c7c0ffbe1ecdab03b912508ca4410fc65e1f242))
20
+
21
+ ### 0.0.2-beta.17 (2024-05-07)
22
+
23
+ ### 0.0.2-beta.16 (2024-05-07)
24
+
25
+ ### 0.0.2-beta.15 (2024-04-25)
26
+
27
+ ### Bug Fixes
28
+
29
+ - child property observable ([#16](https://github.com/JuliusKoronciCH/dantian/issues/16)) ([6d33779](https://github.com/JuliusKoronciCH/dantian/commit/6d33779d5dfe7d51cd7e2e75f0b3a20fe88bcf41))
30
+
31
+ ### 0.0.2-beta.14 (2024-04-25)
32
+
33
+ ### 0.0.2-beta.13 (2024-04-25)
34
+
35
+ ### 0.0.2-beta.12 (2024-04-18)
36
+
37
+ ### 0.0.2-beta.11 (2024-04-08)
38
+
39
+ ### 0.0.2-beta.10 (2024-04-07)
40
+
41
+ ### 0.0.2-beta.9 (2024-04-07)
42
+
43
+ ### 0.0.2-beta.8 (2024-04-04)
44
+
45
+ ### 0.0.2-beta.7 (2024-04-04)
46
+
47
+ ### 0.0.2-beta.6 (2024-04-04)
48
+
49
+ ### 0.0.2-beta.5 (2024-04-04)
50
+
51
+ ### 0.0.2-beta.4 (2024-04-04)
52
+
53
+ ### 0.0.2-beta.3 (2024-04-04)
54
+
55
+ ### 0.0.2-beta.2 (2024-04-04)
package/README.md CHANGED
@@ -1,55 +1,322 @@
1
- **Dantian - React State Management Reimagined**
1
+ # Dantian
2
2
 
3
- [![npm version](https://badge.fury.io/js/dantian.svg)](https://badge.fury.io/js/dantian)
3
+ Event-based state management for React, powered by RxJS. Dantian exposes a small event store API and React hooks that subscribe to specific property paths, so only the parts of UI that care about a given event re-render.
4
4
 
5
- Dantian is an event-based state management library for React applications that delivers pinpoint performance and effortless integration with forms. Say goodbye to unnecessary rerenders and complex state comparisons – Dantian ensures that components only update when they subscribe to specific state changes.
5
+ ## Installation
6
6
 
7
- **The Problem**
7
+ ```bash
8
+ npm install dantian
9
+ ```
8
10
 
9
- Traditional state management solutions in React often lead to performance issues, especially in applications with frequent updates or large forms. Unnecessary rerenders can slow down the user experience.
11
+ ## Compatibility
10
12
 
11
- **Our Solution**
13
+ | Runtime | Supported versions |
14
+ | ------- | ------------------ |
15
+ | React | 18, 19 |
16
+ | RxJS | 7, 8 |
17
+ | Node | 18, 20, 22 |
12
18
 
13
- Dantian's event-driven approach focuses on precision. State updates are triggered by events, and only components subscribed to those events rerender. This provides granular control and optimizes performance where it matters most.
19
+ ## Use Cases & FAQ
14
20
 
15
- **Features**
21
+ ### Quickstart: event store basics
16
22
 
17
- - **useState-like hooks:** Familiar interface for ease of use.
18
- - **Custom events:** Power and flexibility for complex scenarios.
19
- - **Asynchronous state updates:** Handle async patterns gracefully.
20
- - **Optimized form integration:** Tackle large forms without performance compromises.
23
+ ```ts
24
+ // store.ts
25
+ import { createEventStore } from 'dantian';
21
26
 
22
- **Installation**
27
+ export const store = createEventStore({
28
+ count: 0,
29
+ user: { name: 'n/a' },
30
+ });
23
31
 
24
- ```bash
25
- npm install dantian
32
+ export const {
33
+ useStoreValue,
34
+ publish,
35
+ useHydrateStore,
36
+ useIsHydrated,
37
+ reset,
38
+ feed,
39
+ destroy,
40
+ state$,
41
+ systemEvents$,
42
+ } = store;
26
43
  ```
27
44
 
28
- **Basic usage**
29
-
30
- ```TypeScript
31
- import { useEventState } from 'dantian';
45
+ ```tsx
46
+ // Counter.tsx
47
+ import { useStoreValue } from './store';
32
48
 
33
- function MyComponent() {
34
- const [count, setCount] = useEventState('counter', 0);
35
-
36
- const increment = () => setCount(count + 1);
49
+ export function Counter() {
50
+ const [count, setCount] = useStoreValue('count');
37
51
 
38
52
  return (
39
- <div>
40
- <p>Count: {count}</p>
41
- <button onClick={increment}>Increment</button>
42
- </div>
53
+ <button type="button" onClick={() => setCount(count + 1)}>
54
+ Count: {count}
55
+ </button>
43
56
  );
44
57
  }
45
58
  ```
46
59
 
47
- **Getting Started**
60
+ At a glance, `createEventStore` maintains a stream of events and derives state from them. Updates are published by property path (for example, `user.name`), and hooks subscribe to those paths.
61
+
62
+ ### How do I update nested fields?
63
+
64
+ ```tsx
65
+ const [name, setName] = useStoreValue('user.name');
66
+ setName('Ada');
67
+
68
+ // Or outside React:
69
+ publish('user.name', 'Ada');
70
+ ```
71
+
72
+ ### How do I hydrate from an async source?
73
+
74
+ ```ts
75
+ const store = createEventStore(
76
+ { user: { name: 'n/a' } },
77
+ {
78
+ hydrator: async () => {
79
+ const response = await fetch('/api/profile');
80
+ return (await response.json()) as { user: { name: string } };
81
+ },
82
+ },
83
+ );
84
+ ```
85
+
86
+ ### How do I persist state?
87
+
88
+ ```ts
89
+ const store = createEventStore(
90
+ { count: 0 },
91
+ {
92
+ persist: async (state) => {
93
+ localStorage.setItem('dantianState', JSON.stringify(state));
94
+ },
95
+ },
96
+ );
97
+ ```
98
+
99
+ If hydration or persistence fails, you can observe the error via system events
100
+ while console errors remain intact:
101
+
102
+ ```ts
103
+ systemEvents$.subscribe((event) => {
104
+ if (event.type === '@@HYDRATE_ERROR' || event.type === '@@PERSIST_ERROR') {
105
+ console.error('store error', event.payload.error);
106
+ }
107
+ });
108
+ ```
109
+
110
+ ### How do I avoid flicker from concurrent updates?
111
+
112
+ If updates are coming from multiple sources, you can disable local caching per hook:
113
+
114
+ ```tsx
115
+ const [value, setValue] = useStoreValue('profile.name', {
116
+ disableCache: true,
117
+ });
118
+ ```
119
+
120
+ You can also throttle update propagation with `throttle` (milliseconds):
121
+
122
+ ```tsx
123
+ const [user] = useStoreValue('user', { throttle: 50 });
124
+ ```
125
+
126
+ ### How do I reset or feed state?
127
+
128
+ ```ts
129
+ reset({ count: 0, user: { name: 'n/a' } });
130
+ feed({ count: 5, user: { name: 'Julius' } });
131
+ ```
132
+
133
+ ### How do I subscribe outside React?
134
+
135
+ ```ts
136
+ const subscription = state$.subscribe((state) => {
137
+ console.log('state changed', state);
138
+ });
139
+
140
+ subscription.unsubscribe();
141
+ ```
142
+
143
+ ### Dispose of a store
144
+
145
+ If a store is no longer needed, dispose of it to complete internal subjects and
146
+ prevent lingering subscriptions:
147
+
148
+ ```ts
149
+ store.destroy();
150
+ ```
151
+
152
+ After `destroy()`, calls to `publish`, `reset`, `feed`, and the callback from
153
+ `useHydrateStore()` are no-ops.
154
+
155
+ ## Task Guides
156
+
157
+ ### Create a store
158
+
159
+ ```ts
160
+ import { createEventStore } from 'dantian';
161
+
162
+ const store = createEventStore({ count: 0 }, { debug: false });
163
+ ```
164
+
165
+ ### Read and update values in components
166
+
167
+ ```tsx
168
+ const [count, setCount] = store.useStoreValue('count');
169
+ setCount(count + 1);
170
+
171
+ // Or publish directly
172
+ store.publish('count', 42);
173
+ ```
174
+
175
+ `useStoreValue` options:
176
+
177
+ - `disableCache`: bypasses local caching to avoid flicker in edge cases.
178
+ - `throttle`: throttles updates in milliseconds.
179
+ - `throtle`: legacy alias for `throttle` (kept for backward compatibility).
180
+
181
+ ### Hydrate and persist
182
+
183
+ ```ts
184
+ const store = createEventStore(
185
+ { count: 0 },
186
+ {
187
+ hydrator: async () => ({ count: 10 }),
188
+ persist: async (state) => {
189
+ localStorage.setItem('dantianState', JSON.stringify(state));
190
+ },
191
+ },
192
+ );
193
+ ```
194
+
195
+ In React, you can trigger hydration manually:
196
+
197
+ ```tsx
198
+ const hydrate = store.useHydrateStore();
199
+ const isHydrated = store.useIsHydrated();
200
+ ```
201
+
202
+ ### Reset and feed
203
+
204
+ ```ts
205
+ store.reset({ count: 0 });
206
+ store.feed({ count: 5 });
207
+ ```
208
+
209
+ ### Destroy
210
+
211
+ ```ts
212
+ store.destroy();
213
+ ```
214
+
215
+ ### Observe with RxJS
216
+
217
+ ```ts
218
+ const sub = store.getPropertyObservable('count').subscribe((value) => {
219
+ console.log('count changed', value);
220
+ });
221
+
222
+ sub.unsubscribe();
223
+ ```
224
+
225
+ ### Classic store basics
226
+
227
+ If you need a minimal store with selectors and updates, use `buildClassicStore`:
228
+
229
+ ```ts
230
+ import { buildClassicStore } from 'dantian';
231
+
232
+ const classic = await buildClassicStore({ count: 0 });
233
+ const [state, update] = classic.useStore();
234
+ const count = classic.useSelector((s) => s.count);
235
+ update((prev) => ({ ...prev, count: prev.count + 1 }));
236
+ ```
237
+
238
+ With hydration and persistence:
239
+
240
+ ```ts
241
+ const classic = await buildClassicStore({
242
+ beforeLoadState: { count: 0 },
243
+ hydrator: async () => ({ count: 2 }),
244
+ persist: async (state) => {
245
+ localStorage.setItem('classicState', JSON.stringify(state));
246
+ },
247
+ });
248
+ ```
249
+
250
+ ## API Reference
251
+
252
+ ### `createEventStore<T extends object>(initialState, options?)`
253
+
254
+ Options:
255
+
256
+ - `debug?: boolean` — log events and state transitions.
257
+ - `hydrator?: () => Promise<T>` — async hydration source.
258
+ - `persist?: (state: T) => Promise<void>` — persistence callback.
259
+
260
+ Returns:
261
+
262
+ - `useStoreValue<K>(path, options?)`: React hook for reading/updating a property path.
263
+ - `publish(path, payload)`: publish an event for a property path.
264
+ - `getPropertyObservable(path, throttle?)`: RxJS observable for a property path.
265
+ - `useHydrateStore()`: returns a function to emit `@@HYDRATED` with payload.
266
+ - `useIsHydrated()`: returns a boolean hydration flag.
267
+ - `reset(payload)`: emit `@@RESET` system event.
268
+ - `feed(payload)`: emit `@@FEED` system event.
269
+ - `state$`: `BehaviorSubject<T>` with current state.
270
+ - `globalEventStore$`: `BehaviorSubject` of all events.
271
+ - `systemEvents$`: observable of system events (event types starting with `@@`,
272
+ including `@@HYDRATE_ERROR` and `@@PERSIST_ERROR`).
273
+ - `destroy()`: dispose the store, completing internal subjects and stopping
274
+ further publishes.
275
+
276
+ `useStoreValue` options:
277
+
278
+ - `disableCache?: boolean`
279
+ - `throttle?: number`
280
+ - `throtle?: number` (legacy alias)
281
+
282
+ ### `buildClassicStore<T>(defaultState)`
283
+
284
+ `defaultState` can be either a plain initial state or an object with:
285
+
286
+ - `beforeLoadState: T`
287
+ - `hydrator: () => Promise<T>`
288
+ - `persist?: (state: T) => Promise<void>`
289
+
290
+ Returns:
291
+
292
+ - `useStore()`: React hook for `[state, update]`.
293
+ - `useSelector(selector)`: React hook for derived values.
294
+ - `update(updater)`: update function.
295
+ - `getValue()`: current value getter.
296
+ - `subject$`: `BehaviorSubject<T>`.
297
+ - `defaultState`: the provided default state.
298
+
299
+ ### `wuji`
300
+
301
+ Alias of `createEventStore`.
302
+
303
+ ## Troubleshooting
304
+
305
+ - Hydration errors: `systemEvents$` emits `@@HYDRATE_ERROR` and the console logs
306
+ `Failed to hydrate store`. Verify the hydrator resolves with the same shape
307
+ as the initial state and handle thrown errors.
308
+ - Persist errors: `systemEvents$` emits `@@PERSIST_ERROR` and the console logs
309
+ `Failed to persist store`. Ensure your persist callback returns a promise and
310
+ handles storage quotas or serialization failures.
311
+ - Disposed stores still referenced: call `destroy()` when a store is no longer
312
+ used, and avoid calling `publish/reset/feed` afterward.
313
+
314
+ ## 1.0 Migration Notes
48
315
 
49
- **Why Dantian?**
316
+ - No breaking changes are required.
317
+ - `destroy()` is now available to explicitly dispose of stores.
318
+ - `systemEvents$` now includes `@@HYDRATE_ERROR` and `@@PERSIST_ERROR` events.
50
319
 
51
- Performance by design: Avoid unnecessary rerenders, especially in complex forms.
52
- Developer-friendly: Intuitive API and seamless form integration.
53
- **License**
320
+ ## License
54
321
 
55
322
  MIT