synstate 0.1.0 → 0.1.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.
Files changed (235) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +272 -532
  3. package/assets/synstate-icon.png +0 -0
  4. package/dist/core/combine/combine.d.mts +33 -3
  5. package/dist/core/combine/combine.d.mts.map +1 -1
  6. package/dist/core/combine/combine.mjs +34 -4
  7. package/dist/core/combine/combine.mjs.map +1 -1
  8. package/dist/core/combine/merge.d.mts +30 -4
  9. package/dist/core/combine/merge.d.mts.map +1 -1
  10. package/dist/core/combine/merge.mjs +30 -4
  11. package/dist/core/combine/merge.mjs.map +1 -1
  12. package/dist/core/combine/zip.d.mts +28 -3
  13. package/dist/core/combine/zip.d.mts.map +1 -1
  14. package/dist/core/combine/zip.mjs +28 -3
  15. package/dist/core/combine/zip.mjs.map +1 -1
  16. package/dist/core/create/from-array.d.mts +21 -3
  17. package/dist/core/create/from-array.d.mts.map +1 -1
  18. package/dist/core/create/from-array.mjs +21 -3
  19. package/dist/core/create/from-array.mjs.map +1 -1
  20. package/dist/core/create/from-promise.d.mts +29 -7
  21. package/dist/core/create/from-promise.d.mts.map +1 -1
  22. package/dist/core/create/from-promise.mjs +29 -7
  23. package/dist/core/create/from-promise.mjs.map +1 -1
  24. package/dist/core/create/from-subscribable.d.mts +58 -0
  25. package/dist/core/create/from-subscribable.d.mts.map +1 -1
  26. package/dist/core/create/from-subscribable.mjs +58 -0
  27. package/dist/core/create/from-subscribable.mjs.map +1 -1
  28. package/dist/core/create/interval.d.mts +29 -4
  29. package/dist/core/create/interval.d.mts.map +1 -1
  30. package/dist/core/create/interval.mjs +29 -4
  31. package/dist/core/create/interval.mjs.map +1 -1
  32. package/dist/core/create/of.d.mts +22 -3
  33. package/dist/core/create/of.d.mts.map +1 -1
  34. package/dist/core/create/of.mjs +22 -3
  35. package/dist/core/create/of.mjs.map +1 -1
  36. package/dist/core/create/source.d.mts +21 -2
  37. package/dist/core/create/source.d.mts.map +1 -1
  38. package/dist/core/create/source.mjs +2 -2
  39. package/dist/core/create/source.mjs.map +1 -1
  40. package/dist/core/create/timer.d.mts +23 -4
  41. package/dist/core/create/timer.d.mts.map +1 -1
  42. package/dist/core/create/timer.mjs +23 -4
  43. package/dist/core/create/timer.mjs.map +1 -1
  44. package/dist/core/index.d.mts +1 -0
  45. package/dist/core/index.d.mts.map +1 -1
  46. package/dist/core/index.mjs +15 -3
  47. package/dist/core/index.mjs.map +1 -1
  48. package/dist/core/operators/audit-time.d.mts +59 -0
  49. package/dist/core/operators/audit-time.d.mts.map +1 -1
  50. package/dist/core/operators/audit-time.mjs +59 -0
  51. package/dist/core/operators/audit-time.mjs.map +1 -1
  52. package/dist/core/operators/debounce-time.d.mts +22 -2
  53. package/dist/core/operators/debounce-time.d.mts.map +1 -1
  54. package/dist/core/operators/debounce-time.mjs +22 -2
  55. package/dist/core/operators/debounce-time.mjs.map +1 -1
  56. package/dist/core/operators/filter.d.mts +26 -1
  57. package/dist/core/operators/filter.d.mts.map +1 -1
  58. package/dist/core/operators/filter.mjs.map +1 -1
  59. package/dist/core/operators/index.mjs +3 -3
  60. package/dist/core/operators/map-with-index.d.mts +19 -17
  61. package/dist/core/operators/map-with-index.d.mts.map +1 -1
  62. package/dist/core/operators/map-with-index.mjs +21 -23
  63. package/dist/core/operators/map-with-index.mjs.map +1 -1
  64. package/dist/core/operators/merge-map.d.mts +48 -6
  65. package/dist/core/operators/merge-map.d.mts.map +1 -1
  66. package/dist/core/operators/merge-map.mjs +48 -6
  67. package/dist/core/operators/merge-map.mjs.map +1 -1
  68. package/dist/core/operators/pairwise.d.mts +30 -1
  69. package/dist/core/operators/pairwise.d.mts.map +1 -1
  70. package/dist/core/operators/pairwise.mjs +30 -1
  71. package/dist/core/operators/pairwise.mjs.map +1 -1
  72. package/dist/core/operators/scan.d.mts +23 -1
  73. package/dist/core/operators/scan.d.mts.map +1 -1
  74. package/dist/core/operators/scan.mjs +23 -1
  75. package/dist/core/operators/scan.mjs.map +1 -1
  76. package/dist/core/operators/skip-if-no-change.d.mts +26 -2
  77. package/dist/core/operators/skip-if-no-change.d.mts.map +1 -1
  78. package/dist/core/operators/skip-if-no-change.mjs +27 -3
  79. package/dist/core/operators/skip-if-no-change.mjs.map +1 -1
  80. package/dist/core/operators/skip-until.d.mts +50 -0
  81. package/dist/core/operators/skip-until.d.mts.map +1 -1
  82. package/dist/core/operators/skip-until.mjs +50 -0
  83. package/dist/core/operators/skip-until.mjs.map +1 -1
  84. package/dist/core/operators/skip-while.d.mts +48 -1
  85. package/dist/core/operators/skip-while.d.mts.map +1 -1
  86. package/dist/core/operators/skip-while.mjs +50 -5
  87. package/dist/core/operators/skip-while.mjs.map +1 -1
  88. package/dist/core/operators/switch-map.d.mts +39 -5
  89. package/dist/core/operators/switch-map.d.mts.map +1 -1
  90. package/dist/core/operators/switch-map.mjs +39 -5
  91. package/dist/core/operators/switch-map.mjs.map +1 -1
  92. package/dist/core/operators/take-until.d.mts +20 -1
  93. package/dist/core/operators/take-until.d.mts.map +1 -1
  94. package/dist/core/operators/take-until.mjs +20 -1
  95. package/dist/core/operators/take-until.mjs.map +1 -1
  96. package/dist/core/operators/take-while.d.mts +47 -1
  97. package/dist/core/operators/take-while.d.mts.map +1 -1
  98. package/dist/core/operators/take-while.mjs +48 -3
  99. package/dist/core/operators/take-while.mjs.map +1 -1
  100. package/dist/core/operators/throttle-time.d.mts +44 -5
  101. package/dist/core/operators/throttle-time.d.mts.map +1 -1
  102. package/dist/core/operators/throttle-time.mjs +44 -5
  103. package/dist/core/operators/throttle-time.mjs.map +1 -1
  104. package/dist/core/operators/with-buffered-from.d.mts +57 -0
  105. package/dist/core/operators/with-buffered-from.d.mts.map +1 -1
  106. package/dist/core/operators/with-buffered-from.mjs +58 -1
  107. package/dist/core/operators/with-buffered-from.mjs.map +1 -1
  108. package/dist/core/operators/with-current-value-from.d.mts +59 -0
  109. package/dist/core/operators/with-current-value-from.d.mts.map +1 -1
  110. package/dist/core/operators/with-current-value-from.mjs +60 -1
  111. package/dist/core/operators/with-current-value-from.mjs.map +1 -1
  112. package/dist/core/operators/with-initial-value.d.mts +24 -2
  113. package/dist/core/operators/with-initial-value.d.mts.map +1 -1
  114. package/dist/core/operators/with-initial-value.mjs +24 -2
  115. package/dist/core/operators/with-initial-value.mjs.map +1 -1
  116. package/dist/core/predefined/index.d.mts +2 -0
  117. package/dist/core/predefined/index.d.mts.map +1 -0
  118. package/dist/core/predefined/index.mjs +13 -0
  119. package/dist/core/predefined/index.mjs.map +1 -0
  120. package/dist/core/predefined/operators/attach-index.d.mts +8 -0
  121. package/dist/core/predefined/operators/attach-index.d.mts.map +1 -0
  122. package/dist/core/predefined/operators/attach-index.mjs +13 -0
  123. package/dist/core/predefined/operators/attach-index.mjs.map +1 -0
  124. package/dist/core/predefined/operators/index.d.mts +13 -0
  125. package/dist/core/predefined/operators/index.d.mts.map +1 -0
  126. package/dist/core/predefined/operators/index.mjs +13 -0
  127. package/dist/core/predefined/operators/index.mjs.map +1 -0
  128. package/dist/core/predefined/operators/map-optional.d.mts +4 -0
  129. package/dist/core/predefined/operators/map-optional.d.mts.map +1 -0
  130. package/dist/core/predefined/operators/map-optional.mjs +7 -0
  131. package/dist/core/predefined/operators/map-optional.mjs.map +1 -0
  132. package/dist/core/predefined/operators/map-result-err.d.mts +4 -0
  133. package/dist/core/predefined/operators/map-result-err.d.mts.map +1 -0
  134. package/dist/core/predefined/operators/map-result-err.mjs +7 -0
  135. package/dist/core/predefined/operators/map-result-err.mjs.map +1 -0
  136. package/dist/core/predefined/operators/map-result-ok.d.mts +4 -0
  137. package/dist/core/predefined/operators/map-result-ok.d.mts.map +1 -0
  138. package/dist/core/predefined/operators/map-result-ok.mjs +7 -0
  139. package/dist/core/predefined/operators/map-result-ok.mjs.map +1 -0
  140. package/dist/core/predefined/operators/map-to.d.mts +3 -0
  141. package/dist/core/predefined/operators/map-to.d.mts.map +1 -0
  142. package/dist/core/predefined/operators/map-to.mjs +6 -0
  143. package/dist/core/predefined/operators/map-to.mjs.map +1 -0
  144. package/dist/core/predefined/operators/map.d.mts +3 -0
  145. package/dist/core/predefined/operators/map.d.mts.map +1 -0
  146. package/dist/core/predefined/operators/map.mjs +8 -0
  147. package/dist/core/predefined/operators/map.mjs.map +1 -0
  148. package/dist/core/predefined/operators/pluck.d.mts +8 -0
  149. package/dist/core/predefined/operators/pluck.d.mts.map +1 -0
  150. package/dist/core/predefined/operators/pluck.mjs +11 -0
  151. package/dist/core/predefined/operators/pluck.mjs.map +1 -0
  152. package/dist/core/predefined/operators/skip.d.mts +3 -0
  153. package/dist/core/predefined/operators/skip.d.mts.map +1 -0
  154. package/dist/core/predefined/operators/skip.mjs +9 -0
  155. package/dist/core/predefined/operators/skip.mjs.map +1 -0
  156. package/dist/core/predefined/operators/take.d.mts +3 -0
  157. package/dist/core/predefined/operators/take.d.mts.map +1 -0
  158. package/dist/core/predefined/operators/take.mjs +8 -0
  159. package/dist/core/predefined/operators/take.mjs.map +1 -0
  160. package/dist/core/predefined/operators/unwrap-optional.d.mts +4 -0
  161. package/dist/core/predefined/operators/unwrap-optional.d.mts.map +1 -0
  162. package/dist/core/predefined/operators/unwrap-optional.mjs +9 -0
  163. package/dist/core/predefined/operators/unwrap-optional.mjs.map +1 -0
  164. package/dist/core/predefined/operators/unwrap-result-err.d.mts +4 -0
  165. package/dist/core/predefined/operators/unwrap-result-err.d.mts.map +1 -0
  166. package/dist/core/predefined/operators/unwrap-result-err.mjs +7 -0
  167. package/dist/core/predefined/operators/unwrap-result-err.mjs.map +1 -0
  168. package/dist/core/predefined/operators/unwrap-result-ok.d.mts +4 -0
  169. package/dist/core/predefined/operators/unwrap-result-ok.d.mts.map +1 -0
  170. package/dist/core/predefined/operators/unwrap-result-ok.mjs +9 -0
  171. package/dist/core/predefined/operators/unwrap-result-ok.mjs.map +1 -0
  172. package/dist/core/types/observable-family.d.mts +7 -7
  173. package/dist/entry-point.mjs +15 -3
  174. package/dist/entry-point.mjs.map +1 -1
  175. package/dist/index.mjs +15 -3
  176. package/dist/index.mjs.map +1 -1
  177. package/dist/utils/create-event-emitter.d.mts +20 -2
  178. package/dist/utils/create-event-emitter.d.mts.map +1 -1
  179. package/dist/utils/create-event-emitter.mjs +20 -2
  180. package/dist/utils/create-event-emitter.mjs.map +1 -1
  181. package/dist/utils/create-reducer.d.mts +13 -1
  182. package/dist/utils/create-reducer.d.mts.map +1 -1
  183. package/dist/utils/create-reducer.mjs +13 -1
  184. package/dist/utils/create-reducer.mjs.map +1 -1
  185. package/dist/utils/create-state.d.mts +24 -4
  186. package/dist/utils/create-state.d.mts.map +1 -1
  187. package/dist/utils/create-state.mjs +24 -4
  188. package/dist/utils/create-state.mjs.map +1 -1
  189. package/package.json +13 -12
  190. package/src/core/combine/combine.mts +34 -4
  191. package/src/core/combine/merge.mts +30 -4
  192. package/src/core/combine/zip.mts +28 -3
  193. package/src/core/create/from-array.mts +21 -3
  194. package/src/core/create/from-promise.mts +29 -7
  195. package/src/core/create/from-subscribable.mts +58 -0
  196. package/src/core/create/interval.mts +29 -4
  197. package/src/core/create/of.mts +22 -3
  198. package/src/core/create/source.mts +22 -3
  199. package/src/core/create/timer.mts +23 -4
  200. package/src/core/index.mts +1 -0
  201. package/src/core/operators/audit-time.mts +59 -0
  202. package/src/core/operators/debounce-time.mts +22 -2
  203. package/src/core/operators/filter.mts +26 -1
  204. package/src/core/operators/map-with-index.mts +22 -66
  205. package/src/core/operators/merge-map.mts +48 -6
  206. package/src/core/operators/pairwise.mts +30 -1
  207. package/src/core/operators/scan.mts +23 -1
  208. package/src/core/operators/skip-if-no-change.mts +27 -3
  209. package/src/core/operators/skip-until.mts +50 -0
  210. package/src/core/operators/skip-while.mts +49 -16
  211. package/src/core/operators/switch-map.mts +39 -5
  212. package/src/core/operators/take-until.mts +20 -1
  213. package/src/core/operators/take-while.mts +49 -8
  214. package/src/core/operators/throttle-time.mts +44 -5
  215. package/src/core/operators/with-buffered-from.mts +58 -1
  216. package/src/core/operators/with-current-value-from.mts +60 -1
  217. package/src/core/operators/with-initial-value.mts +24 -2
  218. package/src/core/predefined/index.mts +1 -0
  219. package/src/core/predefined/operators/attach-index.mts +13 -0
  220. package/src/core/predefined/operators/index.mts +12 -0
  221. package/src/core/predefined/operators/map-optional.mts +8 -0
  222. package/src/core/predefined/operators/map-result-err.mts +8 -0
  223. package/src/core/predefined/operators/map-result-ok.mts +8 -0
  224. package/src/core/predefined/operators/map-to.mts +5 -0
  225. package/src/core/predefined/operators/map.mts +5 -0
  226. package/src/core/predefined/operators/pluck.mts +12 -0
  227. package/src/core/predefined/operators/skip.mts +10 -0
  228. package/src/core/predefined/operators/take.mts +6 -0
  229. package/src/core/predefined/operators/unwrap-optional.mts +9 -0
  230. package/src/core/predefined/operators/unwrap-result-err.mts +8 -0
  231. package/src/core/predefined/operators/unwrap-result-ok.mts +9 -0
  232. package/src/core/types/observable-family.mts +7 -7
  233. package/src/utils/create-event-emitter.mts +20 -2
  234. package/src/utils/create-reducer.mts +13 -1
  235. package/src/utils/create-state.mts +24 -4
package/README.md CHANGED
@@ -1,25 +1,34 @@
1
- # SyncFlow
1
+ # SynState
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge)
4
- [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge)
5
- [![License](https://img.shields.io/npm/l/ts-data-forge.svg)](./LICENSE)
6
- [![codecov](https://codecov.io/gh/noshiro-pf/ts-data-forge/branch/main/graph/badge.svg?token=69TA40HACZ)](https://codecov.io/gh/noshiro-pf/ts-data-forge)
3
+ <p align="center">
4
+ <img src="./assets/synstate-icon.png" alt="SynState Logo" width="400" />
5
+ </p>
7
6
 
8
- **SyncFlow** is a lightweight, type-safe state management library for TypeScript/JavaScript. Perfect for building reactive global state and event-driven systems in React, Vue, and other frameworks.
7
+ <p align="center">
8
+
9
+ [![npm version](https://img.shields.io/npm/v/synstate.svg)](https://www.npmjs.com/package/synstate)
10
+ [![npm downloads](https://img.shields.io/npm/dm/synstate.svg)](https://www.npmjs.com/package/synstate)
11
+ [![License](https://img.shields.io/npm/l/synstate.svg)](./LICENSE)
12
+ [![codecov](https://codecov.io/gh/noshiro-pf/synstate/graph/badge.svg?token=xrJgTVxMpr)](https://codecov.io/gh/noshiro-pf/synstate)
13
+
14
+ </p>
15
+
16
+ **SynState** is a lightweight, high-performance, type-safe state management library for TypeScript/JavaScript applications. Perfect for building reactive global state and event-driven systems in React, Vue, and other frameworks.
9
17
 
10
18
  ## Features
11
19
 
12
20
  - 🎯 **Simple State Management**: Easy-to-use `createState` and `createReducer` for global state
13
21
  - 📡 **Event System**: Built-in `createValueEmitter`, `createEventEmitter` for event-driven architecture
14
- - 🔄 **Reactive Updates**: Automatic propagation of state changes to subscribers
22
+ - 🔄 **Reactive Updates**: Automatic propagation of state changes to all subscribers
15
23
  - 🎨 **Type-Safe**: Full TypeScript support with precise type inference
16
- - 🚀 **Lightweight**: Minimal bundle size, zero external runtime dependencies
17
- - ⚡ **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
18
- - 🔧 **Flexible**: Simple state management with optional advanced features
24
+ - 🚀 **Lightweight**: Minimal bundle size with only one external runtime dependency ([ts-data-forge](https://www.npmjs.com/package/ts-data-forge))
25
+ - ⚡ **High Performance**: Optimized for fast state updates and minimal re-renders
26
+ - 🌐 **Framework Agnostic**: Works with React, Vue, Svelte, or vanilla JavaScript
27
+ - 🔧 **Observable-based**: Built on Observable pattern similar to RxJS, but with a completely independent implementation from scratch — not a wrapper. Offers optional advanced features like operators (`map`, `filter`, `scan`, `debounceTime`) and combinators (`merge`, `combine`)
19
28
 
20
29
  ## Documentation
21
30
 
22
- - API reference: <https://noshiro-pf.github.io/synstate/>
31
+ - API reference: TBD <!-- <https://noshiro-pf.github.io/synstate/> -->
23
32
 
24
33
  ## Installation
25
34
 
@@ -42,39 +51,33 @@ pnpm add synstate
42
51
  ### Simple State Management
43
52
 
44
53
  ```tsx
45
- import { createState } from 'synstate';
46
-
47
54
  // Create a reactive state
48
- const [state, setState, { updateState }] = createState(0);
55
+ const [state, setState, { updateState, resetState, getSnapshot }] =
56
+ createState(0);
57
+
58
+ const mut_history: number[] = [];
49
59
 
50
60
  // Subscribe to changes (in React components, Vue watchers, etc.)
51
- state.subscribe((count: number) => {
52
- console.log('Count:', count);
61
+ state.subscribe((count) => {
62
+ mut_history.push(count);
53
63
  });
54
64
 
65
+ assert.deepStrictEqual(mut_history, [0]);
66
+
55
67
  // Update state
56
68
  setState(1);
57
69
 
58
- updateState((prev: number) => prev + 1);
59
- ```
70
+ assert.deepStrictEqual(mut_history, [0, 1]);
60
71
 
61
- ### Event Emitter
72
+ updateState((prev) => prev + 2);
62
73
 
63
- ```tsx
64
- import { createValueEmitter } from 'synstate';
74
+ assert.deepStrictEqual(mut_history, [0, 1, 3]);
65
75
 
66
- type User = Readonly<{ id: number; name: string }>;
76
+ assert.isTrue(getSnapshot() === 3);
67
77
 
68
- // Create event emitter
69
- const [userLoggedIn$, emitUserLoggedIn] = createValueEmitter<User>();
78
+ resetState();
70
79
 
71
- // Subscribe to events
72
- userLoggedIn$.subscribe((user) => {
73
- console.log('User logged in:', user.name);
74
- });
75
-
76
- // Emit events
77
- emitUserLoggedIn({ id: 1, name: 'Alice' });
80
+ assert.isTrue(getSnapshot() === 0);
78
81
  ```
79
82
 
80
83
  ### With React
@@ -121,14 +124,97 @@ const UserProfile = (): React.JSX.Element => {
121
124
  };
122
125
  ```
123
126
 
127
+ If you're using React v18 or later:
128
+
129
+ ```tsx
130
+ import * as React from 'react';
131
+ import { createState } from 'synstate';
132
+
133
+ const [userState, setUserState] = createState({
134
+ name: '',
135
+ email: '',
136
+ });
137
+
138
+ const UserProfile = (): React.JSX.Element => {
139
+ const user = React.useSyncExternalStore(
140
+ (onStoreChange: () => void) => {
141
+ const { unsubscribe } = userState.subscribe(onStoreChange);
142
+
143
+ return unsubscribe;
144
+ },
145
+ () => userState.getSnapshot().value,
146
+ );
147
+
148
+ return (
149
+ <div>
150
+ <p>
151
+ {'Name: '}
152
+ {user.name}
153
+ </p>
154
+ <button
155
+ onClick={() => {
156
+ setUserState({
157
+ name: 'Alice',
158
+ email: 'alice@example.com',
159
+ });
160
+ }}
161
+ >
162
+ {'Set User'}
163
+ </button>
164
+ </div>
165
+ );
166
+ };
167
+ ```
168
+
169
+ You can write the equivalent code more concisely using synstate-react-hooks:
170
+
171
+ ```bash
172
+ npm add synstate-react-hooks
173
+ ```
174
+
175
+ ```tsx
176
+ import type * as React from 'react';
177
+ import { createState } from 'synstate-react-hooks';
178
+
179
+ const [useUserState, setUserState] = createState({
180
+ name: '',
181
+ email: '',
182
+ });
183
+
184
+ const UserProfile = (): React.JSX.Element => {
185
+ const user = useUserState();
186
+
187
+ return (
188
+ <div>
189
+ <p>
190
+ {'Name: '}
191
+ {user.name}
192
+ </p>
193
+ <button
194
+ onClick={() => {
195
+ setUserState({
196
+ name: 'Alice',
197
+ email: 'alice@example.com',
198
+ });
199
+ }}
200
+ >
201
+ {'Set User'}
202
+ </button>
203
+ </div>
204
+ );
205
+ };
206
+ ```
207
+
208
+ See also the [synstate-react-hooks README](../synstate-react-hooks/README.md).
209
+
124
210
  ## Core Concepts
125
211
 
126
212
  ### State Management
127
213
 
128
- SyncFlow provides simple, intuitive APIs for managing application state:
214
+ SynState provides simple, intuitive APIs for managing application state:
129
215
 
130
- - **`createState`**: Create mutable state with getter/setter
131
- - **`createReducer`**: Redux-style state management
216
+ - **`createState`**: Create state with getter/setter
217
+ - **`createReducer`**: Create state by reducer and initial value
132
218
  - **`createBooleanState`**: Specialized state for boolean values
133
219
 
134
220
  ### Event System
@@ -142,25 +228,50 @@ Built-in event emitter for event-driven patterns:
142
228
 
143
229
  For advanced use cases, you can use observables to build complex reactive data flows. However, most applications will only need `createState`, `createReducer`, and `createValueEmitter`.
144
230
 
231
+ ## API Reference
232
+
233
+ For complex scenarios, SynState provides observable-based APIs:
234
+
235
+ ### Creation Functions
236
+
237
+ - `source<T>()`: Create a new observable source
238
+ - `of(value)`: Create observable from a single value
239
+ - `fromArray(array)`: Create observable from array
240
+ - `fromPromise(promise)`: Create observable from promise
241
+ - `interval(ms)`: Emit values at intervals
242
+ - `timer(delay)`: Emit after delay
243
+
244
+ ### Operators
245
+
246
+ - `filter(predicate)`: Filter values
247
+ - `map(fn)`: Transform values
248
+ - `scan(reducer, seed)`: Accumulate values
249
+ - `debounceTime(ms)`: Debounce emissions
250
+ - `throttleTime(ms)`: Throttle emissions
251
+ - `skipIfNoChange()`: Skip duplicate values
252
+ - `takeUntil(notifier)`: Complete on notifier emission
253
+
254
+ ### Combination
255
+
256
+ - `combine(observables)`: Combine latest values from multiple sources
257
+ - `merge(observables)`: Merge multiple streams
258
+ - `zip(observables)`: Pair values by index
259
+
260
+ ## Examples
261
+
262
+ ### Global Counter State (React)
263
+
145
264
  ```tsx
146
- import * as React from 'react';
147
- import { createState } from 'synstate';
265
+ import type * as React from 'react';
266
+ import { createState } from 'synstate-react-hooks';
148
267
 
149
268
  // Create global state
150
- export const [counterState, , { updateState, resetState, getSnapshot }] =
269
+ export const [useCounterState, , { updateState, resetState, getSnapshot }] =
151
270
  createState(0);
152
271
 
153
272
  // Component 1
154
273
  const Counter = (): React.JSX.Element => {
155
- const [count, setCount] = React.useState(getSnapshot());
156
-
157
- React.useEffect(() => {
158
- const sub = counterState.subscribe(setCount);
159
-
160
- return () => {
161
- sub.unsubscribe();
162
- };
163
- }, []);
274
+ const count = useCounterState();
164
275
 
165
276
  return (
166
277
  <div>
@@ -191,85 +302,17 @@ const ResetButton = (): React.JSX.Element => (
191
302
  );
192
303
  ```
193
304
 
194
- ## API Reference
195
-
196
- ### State Management (Recommended)
197
-
198
- #### createState
199
-
200
- Create reactive state with getter and setter:
201
-
202
- ```tsx
203
- import * as React from 'react';
204
- import { createEventEmitter, createValueEmitter } from 'synstate';
205
-
206
- // Global events
207
- export const [userLoggedIn$, emitUserLoggedIn] = createValueEmitter<
208
- Readonly<{
209
- id: number;
210
- name: string;
211
- }>
212
- >();
213
-
214
- export const [userLoggedOut$, emitUserLoggedOut] = createEventEmitter();
215
-
216
- // Component that emits events
217
- const LoginButton = (): React.JSX.Element => {
218
- const handleLogin = React.useCallback(() => {
219
- (async () => {
220
- const user = await loginUser();
221
-
222
- emitUserLoggedIn(user);
223
- })().catch(() => {});
224
- }, []);
225
-
226
- return <button onClick={handleLogin}>{'Login'}</button>;
227
- };
228
-
229
- // Component that listens to events
230
- const NotificationPage = (): React.JSX.Element => {
231
- const [message, setMessage] = React.useState('');
232
-
233
- React.useEffect(() => {
234
- const sub1 = userLoggedIn$.subscribe((user) => {
235
- setMessage(`Welcome, ${user.name}!`);
236
- });
237
-
238
- const sub2 = userLoggedOut$.subscribe(() => {
239
- setMessage('Logged out');
240
- });
241
-
242
- return () => {
243
- sub1.unsubscribe();
244
-
245
- sub2.unsubscribe();
246
- };
247
- }, []);
248
-
249
- return message !== '' ? (
250
- <div className={'notification'}>{message}</div>
251
- ) : (
252
- <>{null}</>
253
- );
254
- };
255
-
256
- const loginUser = async (): Promise<
257
- Readonly<{
258
- id: number;
259
- name: string;
260
- }>
261
- > => ({ id: 1, name: 'Alice' });
262
- ```
263
-
264
- #### createBooleanState
265
-
266
- Specialized state for boolean values:
305
+ ### Todo List with Reducer (React)
267
306
 
268
307
  ```tsx
269
308
  import * as React from 'react';
270
- import { createReducer } from 'synstate';
309
+ import { createReducer } from 'synstate-react-hooks';
271
310
 
272
- type Todo = Readonly<{ id: number; text: string; done: boolean }>;
311
+ type Todo = Readonly<{
312
+ id: number;
313
+ text: string;
314
+ done: boolean;
315
+ }>;
273
316
 
274
317
  type Action = Readonly<
275
318
  | { type: 'add'; text: string }
@@ -277,10 +320,9 @@ type Action = Readonly<
277
320
  | { type: 'remove'; id: number }
278
321
  >;
279
322
 
280
- const [todoState, dispatch, getSnapshot] = createReducer<
281
- readonly Todo[],
282
- Action
283
- >((todos, action) => {
323
+ const initialTodos: readonly Todo[] = [];
324
+
325
+ const reducer = (todos: readonly Todo[], action: Action): readonly Todo[] => {
284
326
  switch (action.type) {
285
327
  case 'add':
286
328
  return [
@@ -291,128 +333,109 @@ const [todoState, dispatch, getSnapshot] = createReducer<
291
333
  done: false,
292
334
  },
293
335
  ];
336
+
294
337
  case 'toggle':
295
338
  return todos.map((t) =>
296
339
  t.id === action.id ? { ...t, done: !t.done } : t,
297
340
  );
341
+
298
342
  case 'remove':
299
343
  return todos.filter((t) => t.id !== action.id);
300
344
  }
301
- }, []);
345
+ };
302
346
 
303
- const TodoList = (): React.JSX.Element => {
304
- const [todos, setTodos] = React.useState(getSnapshot());
347
+ const [useTodoState, dispatch] = createReducer<readonly Todo[], Action>(
348
+ reducer,
349
+ initialTodos,
350
+ );
305
351
 
306
- React.useEffect(() => {
307
- const sub = todoState.subscribe(setTodos);
352
+ const addTodo = (): void => {
353
+ dispatch({
354
+ type: 'add',
355
+ text: 'New Todo',
356
+ });
357
+ };
308
358
 
309
- return () => {
310
- sub.unsubscribe();
311
- };
312
- }, []);
359
+ const TodoList = (): React.JSX.Element => {
360
+ const todos = useTodoState();
361
+
362
+ const todosWithHandler = React.useMemo(
363
+ () =>
364
+ todos.map((todo) => ({
365
+ ...todo,
366
+ onToggle: () => {
367
+ dispatch({
368
+ type: 'toggle',
369
+ id: todo.id,
370
+ });
371
+ },
372
+ onRemove: () => {
373
+ dispatch({
374
+ type: 'remove',
375
+ id: todo.id,
376
+ });
377
+ },
378
+ })),
379
+ [todos],
380
+ );
313
381
 
314
382
  return (
315
383
  <div>
316
- {todos.map((todo) => (
384
+ {todosWithHandler.map((todo) => (
317
385
  <div key={todo.id}>
318
386
  <input
319
387
  checked={todo.done}
320
388
  type={'checkbox'}
321
- onChange={() => {
322
- dispatch({
323
- type: 'toggle',
324
- id: todo.id,
325
- });
326
- }}
389
+ onChange={todo.onToggle}
327
390
  />
328
391
  <span>{todo.text}</span>
392
+ <button onClick={todo.onRemove}>{'Remove'}</button>
329
393
  </div>
330
394
  ))}
331
- <button
332
- onClick={() => {
333
- dispatch({
334
- type: 'add',
335
- text: 'New Todo',
336
- });
337
- }}
338
- >
339
- {'Add Todo'}
340
- </button>
395
+ <button onClick={addTodo}>{'Add Todo'}</button>
341
396
  </div>
342
397
  );
343
398
  };
344
399
  ```
345
400
 
346
- #### createReducer
347
-
348
- Create state with reducer pattern (like Redux):
401
+ ### Boolean State (Dark Mode)
349
402
 
350
403
  ```tsx
351
404
  import * as React from 'react';
352
- import { createBooleanState } from 'synstate';
405
+ import { createBooleanState } from 'synstate-react-hooks';
353
406
 
354
- export const [darkModeState, { toggle, getSnapshot }] =
407
+ export const [useDarkModeState, { toggle: toggleDarkMode }] =
355
408
  createBooleanState(false);
356
409
 
357
410
  const ThemeToggle = (): React.JSX.Element => {
358
- const [isDark, setIsDark] = React.useState(getSnapshot());
359
-
360
- React.useEffect(() => {
361
- const sub = darkModeState.subscribe(setIsDark);
362
-
363
- return () => {
364
- sub.unsubscribe();
365
- };
366
- }, []);
411
+ const isDark = useDarkModeState();
367
412
 
368
413
  React.useEffect(() => {
369
414
  document.body.className = isDark ? 'dark' : 'light';
370
415
  }, [isDark]);
371
416
 
372
- return (
373
- <button
374
- onClick={() => {
375
- toggle();
376
- }}
377
- >
378
- {isDark ? '🌙' : '☀️'}
379
- </button>
380
- );
417
+ return <button onClick={toggleDarkMode}>{isDark ? '🌙' : '☀️'}</button>;
381
418
  };
382
419
  ```
383
420
 
384
- ### Event System
385
-
386
- #### createValueEmitter
387
-
388
- Create type-safe event emitter with payload:
421
+ ### Cross-Component Communication
389
422
 
390
423
  ```tsx
391
424
  import * as React from 'react';
392
- import { createEventEmitter, createState, createValueEmitter } from 'synstate';
393
-
394
- // Events
395
- const [onItemAdded$, emitItemAdded] = createValueEmitter<string>();
396
-
397
- const [onClearAll$, emitClearAll] = createEventEmitter();
425
+ import { createState } from 'synstate-react-hooks';
398
426
 
399
427
  // State
400
- const [itemsState, setItemsState, { updateState, getSnapshot }] = createState<
401
- readonly string[]
402
- >([]);
428
+ const [useItemsState, _, { updateState, resetState: resetItemsState }] =
429
+ createState<readonly string[]>([]);
403
430
 
404
431
  // Setup event handlers
405
- onItemAdded$.subscribe((item) => {
432
+ const addItem = (item: string): void => {
406
433
  updateState((items: readonly string[]) => [...items, item]);
407
- });
408
-
409
- onClearAll$.subscribe(() => {
410
- setItemsState([]);
411
- });
434
+ };
412
435
 
413
436
  // Component 1: Add items
414
437
  const ItemInput = (): React.JSX.Element => {
415
- const [input, setInput] = React.useState('');
438
+ const [input, setInput] = React.useState<string>('');
416
439
 
417
440
  return (
418
441
  <div>
@@ -424,7 +447,7 @@ const ItemInput = (): React.JSX.Element => {
424
447
  />
425
448
  <button
426
449
  onClick={() => {
427
- emitItemAdded(input);
450
+ addItem(input);
428
451
 
429
452
  setInput('');
430
453
  }}
@@ -437,15 +460,7 @@ const ItemInput = (): React.JSX.Element => {
437
460
 
438
461
  // Component 2: Display items
439
462
  const ItemList = (): React.JSX.Element => {
440
- const [items, setItems] = React.useState(getSnapshot());
441
-
442
- React.useEffect(() => {
443
- const sub = itemsState.subscribe(setItems);
444
-
445
- return () => {
446
- sub.unsubscribe();
447
- };
448
- }, []);
463
+ const items = useItemsState();
449
464
 
450
465
  return (
451
466
  <div>
@@ -454,33 +469,36 @@ const ItemList = (): React.JSX.Element => {
454
469
  <li key={i}>{item}</li>
455
470
  ))}
456
471
  </ul>
457
- <button onClick={emitClearAll}>{'Clear All'}</button>
472
+ <button onClick={resetItemsState}>{'Clear All'}</button>
458
473
  </div>
459
474
  );
460
475
  };
461
476
  ```
462
477
 
463
- #### createEventEmitter
478
+ // Events
464
479
 
465
- Create event emitter without payload:
480
+ ### Advanced: Search with Debounce
466
481
 
467
482
  ```tsx
468
- import * as React from 'react';
483
+ import type * as React from 'react';
469
484
  import {
470
485
  createState,
471
486
  debounceTime,
472
487
  filter,
473
488
  fromPromise,
474
- type Observable,
489
+ type InitializedObservable,
490
+ map,
475
491
  switchMap,
492
+ withInitialValue,
476
493
  } from 'synstate';
494
+ import { useObservableValue } from 'synstate-react-hooks';
477
495
  import { Result } from 'ts-data-forge';
478
496
 
479
497
  const [searchState, setSearchState] = createState('');
480
498
 
481
- // Advanced reactive pipeline (optional feature)
482
- const searchResults$: Observable<
483
- Result<readonly Readonly<{ id: string; name: string }>[], unknown>
499
+ // Advanced reactive pipeline with debounce and filtering
500
+ const searchResults$: InitializedObservable<
501
+ readonly Readonly<{ id: string; name: string }>[]
484
502
  > = searchState
485
503
  .pipe(debounceTime(300))
486
504
  .pipe(filter((query) => query.length > 2))
@@ -495,24 +513,13 @@ const searchResults$: Observable<
495
513
  ),
496
514
  ),
497
515
  ),
498
- );
516
+ )
517
+ .pipe(filter((res) => Result.isOk(res)))
518
+ .pipe(map((res) => Result.unwrapOk(res)))
519
+ .pipe(withInitialValue([]));
499
520
 
500
521
  const SearchBox = (): React.JSX.Element => {
501
- const [results, setResults] = React.useState<
502
- readonly Readonly<{ id: string; name: string }>[]
503
- >([]);
504
-
505
- React.useEffect(() => {
506
- const sub = searchResults$.subscribe((result) => {
507
- if (Result.isOk(result)) {
508
- setResults(result.value);
509
- }
510
- });
511
-
512
- return () => {
513
- sub.unsubscribe();
514
- };
515
- }, []);
522
+ const searchResults = useObservableValue(searchResults$);
516
523
 
517
524
  return (
518
525
  <div>
@@ -523,7 +530,7 @@ const SearchBox = (): React.JSX.Element => {
523
530
  }}
524
531
  />
525
532
  <ul>
526
- {results.map((item) => (
533
+ {searchResults.map((item) => (
527
534
  <li key={item.id}>{item.name}</li>
528
535
  ))}
529
536
  </ul>
@@ -532,315 +539,60 @@ const SearchBox = (): React.JSX.Element => {
532
539
  };
533
540
  ```
534
541
 
535
- ### Advanced Features (Optional)
536
-
537
- For complex scenarios, SyncFlow provides observable-based APIs:
538
-
539
- #### Creation Functions
540
-
541
- - `source<T>()`: Create a new observable source
542
- - `of(value)`: Create observable from a single value
543
- - `fromArray(array)`: Create observable from array
544
- - `fromPromise(promise)`: Create observable from promise
545
- - `interval(ms)`: Emit values at intervals
546
- - `timer(delay)`: Emit after delay
547
-
548
- #### Operators
549
-
550
- - `filter(predicate)`: Filter values
551
- - `map(fn)`: Transform values
552
- - `scan(reducer, seed)`: Accumulate values
553
- - `debounceTime(ms)`: Debounce emissions
554
- - `throttleTime(ms)`: Throttle emissions
555
- - `skipIfNoChange()`: Skip duplicate values
556
- - `takeUntil(notifier)`: Complete on notifier emission
557
-
558
- #### Combination
559
-
560
- - `combine(observables)`: Combine latest values from multiple sources
561
- - `merge(observables)`: Merge multiple streams
562
- - `zip(observables)`: Pair values by index
563
-
564
- ## Examples
565
-
566
- ### Global Counter State (React)
567
-
568
- ```tsx
569
- import { createState } from 'synstate';
570
- import { useState, useEffect } from 'react';
571
-
572
- // Create global state
573
- export const counterState = createState(0);
574
-
575
- // Component 1
576
- function Counter() {
577
- const [count, setCount] = useState(counterState.getSnapshot());
578
-
579
- useEffect(() => {
580
- const sub = counterState.state.subscribe(setCount);
581
- return () => sub.unsubscribe();
582
- }, []);
583
-
584
- return (
585
- <div>
586
- <p>Count: {count}</p>
587
- <button onClick={() => counterState.updateState((n) => n + 1)}>
588
- Increment
589
- </button>
590
- </div>
591
- );
592
- }
593
-
594
- // Component 2 (synced automatically)
595
- function ResetButton() {
596
- return <button onClick={() => counterState.resetState()}>Reset</button>;
597
- }
598
- ```
599
-
600
- ### Event-Driven Architecture (React)
601
-
602
- ```tsx
603
- import { createValueEmitter } from 'synstate';
604
- import { useEffect } from 'react';
605
-
606
- // Global events
607
- export const [userLoggedIn$, emitUserLoggedIn] = createValueEmitter<{
608
- id: number;
609
- name: string;
610
- }>();
611
-
612
- export const [userLoggedOut$, emitUserLoggedOut] = createEventEmitter();
613
-
614
- // Component that emits events
615
- function LoginButton() {
616
- const handleLogin = async () => {
617
- const user = await loginUser();
618
- emitUserLoggedIn(user);
619
- };
620
-
621
- return <button onClick={handleLogin}>Login</button>;
622
- }
623
-
624
- // Component that listens to events
625
- function Notification() {
626
- const [message, setMessage] = useState('');
627
-
628
- useEffect(() => {
629
- const sub1 = userLoggedIn$.subscribe((user) => {
630
- setMessage(`Welcome, ${user.name}!`);
631
- });
632
-
633
- const sub2 = userLoggedOut$.subscribe(() => {
634
- setMessage('Logged out');
635
- });
636
-
637
- return () => {
638
- sub1.unsubscribe();
639
- sub2.unsubscribe();
640
- };
641
- }, []);
642
-
643
- return message ? <div className="notification">{message}</div> : null;
644
- }
645
- ```
646
-
647
- ### Todo List with Reducer (React)
542
+ ### Advanced: Event Emitter with Throttle
648
543
 
649
544
  ```tsx
650
- import { createReducer } from 'synstate';
651
- import { useState, useEffect } from 'react';
652
-
653
- type Todo = { id: number; text: string; done: boolean };
654
- type Action =
655
- | { type: 'add'; text: string }
656
- | { type: 'toggle'; id: number }
657
- | { type: 'remove'; id: number };
658
-
659
- const todoState = createReducer<Todo[], Action>((todos, action) => {
660
- switch (action.type) {
661
- case 'add':
662
- return [
663
- ...todos,
664
- {
665
- id: Date.now(),
666
- text: action.text,
667
- done: false,
668
- },
669
- ];
670
- case 'toggle':
671
- return todos.map((t) =>
672
- t.id === action.id ? { ...t, done: !t.done } : t,
673
- );
674
- case 'remove':
675
- return todos.filter((t) => t.id !== action.id);
676
- }
677
- }, []);
678
-
679
- function TodoList() {
680
- const [todos, setTodos] = useState(todoState.getSnapshot());
681
-
682
- useEffect(() => {
683
- const sub = todoState.state.subscribe(setTodos);
684
- return () => sub.unsubscribe();
685
- }, []);
545
+ import { createEventEmitter, throttleTime } from 'synstate';
686
546
 
687
- return (
688
- <div>
689
- {todos.map((todo) => (
690
- <div key={todo.id}>
691
- <input
692
- type="checkbox"
693
- checked={todo.done}
694
- onChange={() =>
695
- todoState.dispatch({
696
- type: 'toggle',
697
- id: todo.id,
698
- })
699
- }
700
- />
701
- <span>{todo.text}</span>
702
- </div>
703
- ))}
704
- <button
705
- onClick={() =>
706
- todoState.dispatch({
707
- type: 'add',
708
- text: 'New Todo',
709
- })
710
- }
711
- >
712
- Add Todo
713
- </button>
714
- </div>
715
- );
716
- }
717
- ```
718
-
719
- ### Boolean State (Dark Mode)
720
-
721
- ```tsx
722
- import { createBooleanState } from 'synstate';
723
- import { useState, useEffect } from 'react';
724
-
725
- export const darkModeState = createBooleanState(false);
726
-
727
- function ThemeToggle() {
728
- const [isDark, setIsDark] = useState(darkModeState.getSnapshot());
729
-
730
- useEffect(() => {
731
- const sub = darkModeState.state.subscribe(setIsDark);
732
- return () => sub.unsubscribe();
733
- }, []);
734
-
735
- useEffect(() => {
736
- document.body.className = isDark ? 'dark' : 'light';
737
- }, [isDark]);
738
-
739
- return (
740
- <button onClick={() => darkModeState.toggle()}>
741
- {isDark ? '🌙' : '☀️'}
742
- </button>
743
- );
744
- }
745
- ```
746
-
747
- ### Cross-Component Communication
748
-
749
- ```tsx
750
- import { createValueEmitter, createState } from 'synstate';
751
- import { useState, useEffect } from 'react';
752
- ```
753
-
754
- // Events
755
-
756
- ### Advanced: Search with Debounce
757
-
758
- ```tsx
759
- import * as React from 'react';
760
- import {
761
- createState,
762
- debounceTime,
763
- filter,
764
- fromPromise,
765
- type Observable,
766
- switchMap,
767
- } from 'synstate';
768
- import { Result } from 'ts-data-forge';
769
-
770
- const [searchState, setSearchState] = createState('');
771
-
772
- // Advanced reactive pipeline (optional feature)
773
- const searchResults$: Observable<
774
- Result<readonly Readonly<{ id: string; name: string }>[], unknown>
775
- > = searchState
776
- .pipe(debounceTime(300))
777
- .pipe(filter((query) => query.length > 2))
778
- .pipe(
779
- switchMap((query) =>
780
- fromPromise(
781
- fetch(`/api/search?q=${query}`).then(
782
- (r) =>
783
- r.json() as Promise<
784
- readonly Readonly<{ id: string; name: string }>[]
785
- >,
786
- ),
787
- ),
788
- ),
789
- );
547
+ // Create event emitter
548
+ const [refreshClicked, onRefreshClick] = createEventEmitter();
790
549
 
791
- const SearchBox = (): React.JSX.Element => {
792
- const [results, setResults] = React.useState<
793
- readonly Readonly<{ id: string; name: string }>[]
794
- >([]);
550
+ // Subscribe to events
551
+ refreshClicked.subscribe(() => {
552
+ console.log('Refresh Clicked');
553
+ });
795
554
 
796
- React.useEffect(() => {
797
- const sub = searchResults$.subscribe((result) => {
798
- if (Result.isOk(result)) {
799
- setResults(result.value);
800
- }
801
- });
555
+ // Throttle refresh clicks to prevent rapid successive executions
556
+ const throttledRefresh = refreshClicked.pipe(throttleTime(2000));
802
557
 
803
- return () => {
804
- sub.unsubscribe();
805
- };
806
- }, []);
558
+ throttledRefresh.subscribe(() => {
559
+ console.log('Executing refresh...');
560
+ // Actual refresh logic here
561
+ // This will be called at most once every 2 seconds
562
+ });
807
563
 
808
- return (
809
- <div>
810
- <input
811
- placeholder={'Search...'}
812
- onChange={(e) => {
813
- setSearchState(e.target.value);
814
- }}
815
- />
816
- <ul>
817
- {results.map((item) => (
818
- <li key={item.id}>{item.name}</li>
819
- ))}
820
- </ul>
821
- </div>
822
- );
823
- };
564
+ const DataTable = (): React.JSX.Element => (
565
+ <div>
566
+ <button onClick={onRefreshClick}>{'Refresh'}</button>
567
+ <p>
568
+ {'Data: '}
569
+ {/* Display data here */}
570
+ </p>
571
+ </div>
572
+ );
824
573
  ```
825
574
 
826
- ## Why SyncFlow?
575
+ ## Why SynState?
827
576
 
828
577
  ### Simple State Management, Not Complex Reactive Programming
829
578
 
830
- Unlike RxJS, which can make code harder to read with many operators and complex streams, SyncFlow focuses on **simple, readable state management and event handling**. Most applications only need `createState`, `createReducer`, and `createValueEmitter` - clean, straightforward APIs that developers understand immediately.
579
+ SynState is a state management library for web frontends, similar to Redux, Jotai, Zustand, and MobX. It provides APIs for creating and managing global state across your application.
580
+
581
+ Under the hood, SynState is built on Observable patterns similar to those provided by RxJS. However, unlike RxJS, which can make code harder to read with many operators and complex streams, SynState focuses on **simple, readable state management and event handling**. Most applications only need `createState`, `createReducer`, and simple operators/combinators like `combine` and `map` — clean, straightforward APIs that developers understand immediately.
831
582
 
832
583
  **Advanced reactive features are optional** and only used when you actually need them (like debouncing search input). The library doesn't force you into a reactive programming mindset.
833
584
 
834
585
  ### Key Differences from RxJS
835
586
 
836
- - **Focus on State & Events**: Designed for state management and event-driven architecture
837
- - **Simpler API**: Most use cases covered by `createState`, `createReducer`, and `createValueEmitter`
587
+ - **Focus on State Management**: Designed specifically for state management, not just asynchronous event processing
588
+ - **InitializedObservable**: Provides `InitializedObservable` which always holds an initial value, making it ideal for representing state
589
+ - **Simpler API**: Most use cases are covered by `createState`, `createReducer`, and `createEventEmitter`
838
590
  - **Better Readability**: No need for complex operator chains in everyday code
839
- - **Optional Complexity**: Advanced features available when needed
591
+ - **Optional Complexity**: Advanced features available to manipulate Observables when needed
840
592
 
841
593
  ### Use Cases
842
594
 
843
- **Use SyncFlow when you need:**
595
+ **Use SynState when you need:**
844
596
 
845
597
  - ✅ Global state management across components
846
598
  - ✅ Event-driven communication between components
@@ -850,24 +602,12 @@ Unlike RxJS, which can make code harder to read with many operators and complex
850
602
 
851
603
  **Consider other solutions when:**
852
604
 
853
- - ❌ You need complex stream processing (use RxJS)
605
+ - ❌ You need state in a React component (use React hooks `useState`, `useReducer`)
854
606
  - ❌ Your app is simple enough for React Context alone
855
607
 
856
608
  ## Type Safety
857
609
 
858
- SyncFlow maintains full type information:
859
-
860
- ```tsx
861
- const userState = createState({ name: 'Alice', age: 25 });
862
- // state type: Observable<{ name: string; age: number }>
863
-
864
- const snapshot = userState.getSnapshot();
865
- // snapshot type: { name: string; age: number }
866
-
867
- const [onClick$, emitClick] = createValueEmitter<MouseEvent>();
868
- // onClick$ type: Observable<MouseEvent>
869
- // emitClick type: (event: MouseEvent) => void
870
- ```
610
+ SynState maintains full type information.
871
611
 
872
612
  ## License
873
613