@push.rocks/smartstate 2.0.20 → 2.0.22
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/dist_bundle/bundle.js +1 -1
- package/dist_bundle/bundle.js.map +2 -2
- package/dist_ts/smartstate.classes.stateaction.d.ts +1 -1
- package/dist_ts/smartstate.classes.stateaction.js +2 -2
- package/package.json +1 -1
- package/readme.hints.md +39 -1
- package/readme.md +111 -12
- package/ts/smartstate.classes.stateaction.ts +2 -2
|
@@ -9,5 +9,5 @@ export declare class StateAction<TStateType, TActionPayloadType> {
|
|
|
9
9
|
statePartRef: StatePart<any, any>;
|
|
10
10
|
actionDef: IActionDef<TStateType, TActionPayloadType>;
|
|
11
11
|
constructor(statePartRef: StatePart<any, any>, actionDef: IActionDef<TStateType, TActionPayloadType>);
|
|
12
|
-
trigger(payload: TActionPayloadType):
|
|
12
|
+
trigger(payload: TActionPayloadType): Promise<TStateType>;
|
|
13
13
|
}
|
|
@@ -9,7 +9,7 @@ export class StateAction {
|
|
|
9
9
|
this.actionDef = actionDef;
|
|
10
10
|
}
|
|
11
11
|
trigger(payload) {
|
|
12
|
-
this.statePartRef.dispatchAction(this, payload);
|
|
12
|
+
return this.statePartRef.dispatchAction(this, payload);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRzdGF0ZS5jbGFzc2VzLnN0YXRlYWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0seUJBQXlCLENBQUM7QUFDbkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBTTlEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVc7SUFDdEIsWUFDUyxZQUFpQyxFQUNqQyxTQUFxRDtRQURyRCxpQkFBWSxHQUFaLFlBQVksQ0FBcUI7UUFDakMsY0FBUyxHQUFULFNBQVMsQ0FBNEM7SUFDM0QsQ0FBQztJQUVHLE9BQU8sQ0FBQyxPQUEyQjtRQUN4QyxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN6RCxDQUFDO0NBQ0YifQ==
|
package/package.json
CHANGED
package/readme.hints.md
CHANGED
|
@@ -1 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
# Smartstate Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Current API (as of analysis)
|
|
4
|
+
|
|
5
|
+
### State Part Initialization
|
|
6
|
+
- State parts can be created with different init modes: 'soft', 'mandatory', 'force', 'persistent'
|
|
7
|
+
- Persistent mode automatically calls init() internally - no need to call it manually
|
|
8
|
+
- WebStore integration for persistent state uses IndexedDB
|
|
9
|
+
|
|
10
|
+
### Actions
|
|
11
|
+
- Actions are created with `createAction()` method
|
|
12
|
+
- Two ways to dispatch actions:
|
|
13
|
+
1. `stateAction.trigger(payload)` - returns Promise<TStatePayload>
|
|
14
|
+
2. `await statePart.dispatchAction(stateAction, payload)` - returns Promise<TStatePayload>
|
|
15
|
+
- Both methods now return the same Promise, providing flexibility in usage
|
|
16
|
+
|
|
17
|
+
### State Management Methods
|
|
18
|
+
- `select()` - returns Observable with startWith current state
|
|
19
|
+
- `waitUntilPresent()` - waits for specific state condition
|
|
20
|
+
- `stateSetup()` - async state initialization with cumulative defer
|
|
21
|
+
- `notifyChangeCumulative()` - defers notification to end of call stack (no callback parameter)
|
|
22
|
+
|
|
23
|
+
### State Hash Detection
|
|
24
|
+
- Uses SHA256 hash to detect actual state changes
|
|
25
|
+
- Bug: Currently stores the state object itself as hash instead of the actual hash
|
|
26
|
+
- This prevents proper duplicate notification prevention
|
|
27
|
+
|
|
28
|
+
### Type System
|
|
29
|
+
- Can use either enums or string literal types for state part names
|
|
30
|
+
- Test uses simple string types: `type TMyStateParts = 'testStatePart'`
|
|
31
|
+
|
|
32
|
+
## Fixed Issues in Documentation
|
|
33
|
+
1. Updated trigger() to return Promise (API enhancement)
|
|
34
|
+
2. Added dispatchAction as alternative method
|
|
35
|
+
3. Corrected notifyChangeCumulative usage
|
|
36
|
+
4. Clarified persistent mode auto-init
|
|
37
|
+
5. Added stateSetup documentation
|
|
38
|
+
6. Fixed state hash detection description
|
|
39
|
+
7. Both trigger() and dispatchAction() now return Promise for consistency
|
package/readme.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @push.rocks/smartstate
|
|
2
|
-
|
|
2
|
+
A package for handling and managing state in applications
|
|
3
3
|
|
|
4
4
|
## Install
|
|
5
5
|
|
|
6
|
-
To install `@push.rocks/smartstate`, you can use
|
|
6
|
+
To install `@push.rocks/smartstate`, you can use pnpm (Performant Node Package Manager). Run the following command in your terminal:
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
|
|
9
|
+
pnpm install @push.rocks/smartstate --save
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
This will add `@push.rocks/smartstate` to your project's dependencies.
|
|
@@ -31,17 +31,30 @@ import { Smartstate, StatePart, StateAction } from '@push.rocks/smartstate';
|
|
|
31
31
|
const myAppSmartState = new Smartstate<YourStatePartNamesEnum>();
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### Understanding Init Modes
|
|
35
|
+
|
|
36
|
+
When creating state parts, you can specify different initialization modes:
|
|
37
|
+
|
|
38
|
+
- **`'soft'`** - Allows existing state parts to remain (default behavior)
|
|
39
|
+
- **`'mandatory'`** - Fails if there's an existing state part with the same name
|
|
40
|
+
- **`'force'`** - Overwrites any existing state part
|
|
41
|
+
- **`'persistent'`** - Enables WebStore persistence using IndexedDB
|
|
42
|
+
|
|
34
43
|
### Defining State Parts
|
|
35
44
|
|
|
36
45
|
State parts represent separable sections of your state, making it easier to manage and modularize. For example, you may have a state part for user data and another for application settings.
|
|
37
46
|
|
|
38
|
-
Define
|
|
47
|
+
Define state part names using either enums or string literal types:
|
|
39
48
|
|
|
40
49
|
```typescript
|
|
50
|
+
// Option 1: Using enums
|
|
41
51
|
enum AppStateParts {
|
|
42
|
-
UserState,
|
|
43
|
-
SettingsState
|
|
52
|
+
UserState = 'UserState',
|
|
53
|
+
SettingsState = 'SettingsState'
|
|
44
54
|
}
|
|
55
|
+
|
|
56
|
+
// Option 2: Using string literal types (simpler approach)
|
|
57
|
+
type AppStateParts = 'UserState' | 'SettingsState';
|
|
45
58
|
```
|
|
46
59
|
|
|
47
60
|
Now, let's create a state part within our `myAppSmartState` instance:
|
|
@@ -54,8 +67,11 @@ interface IUserState {
|
|
|
54
67
|
|
|
55
68
|
const userStatePart = await myAppSmartState.getStatePart<IUserState>(
|
|
56
69
|
AppStateParts.UserState,
|
|
57
|
-
{ isLoggedIn: false } // Initial state
|
|
70
|
+
{ isLoggedIn: false }, // Initial state
|
|
71
|
+
'soft' // Init mode (optional, defaults to 'soft')
|
|
58
72
|
);
|
|
73
|
+
|
|
74
|
+
// Note: Persistent state parts are automatically initialized internally
|
|
59
75
|
```
|
|
60
76
|
|
|
61
77
|
### Subscribing to State Changes
|
|
@@ -93,27 +109,110 @@ const loginUserAction = userStatePart.createAction<ILoginPayload>(async (statePa
|
|
|
93
109
|
|
|
94
110
|
// Dispatch the action to update the state
|
|
95
111
|
loginUserAction.trigger({ username: 'johnDoe' });
|
|
112
|
+
// or await the result
|
|
113
|
+
const newState = await loginUserAction.trigger({ username: 'johnDoe' });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Dispatching Actions
|
|
117
|
+
|
|
118
|
+
There are two ways to dispatch actions:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Method 1: Using trigger on the action (returns promise)
|
|
122
|
+
const newState = await loginUserAction.trigger({ username: 'johnDoe' });
|
|
123
|
+
// or fire and forget
|
|
124
|
+
loginUserAction.trigger({ username: 'johnDoe' });
|
|
125
|
+
|
|
126
|
+
// Method 2: Using dispatchAction on the state part (returns promise)
|
|
127
|
+
const newState = await userStatePart.dispatchAction(loginUserAction, { username: 'johnDoe' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Both methods return a Promise with the new state, giving you flexibility in how you handle the result.
|
|
131
|
+
|
|
132
|
+
### Additional State Methods
|
|
133
|
+
|
|
134
|
+
`StatePart` provides several useful methods for state management:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Wait for a specific state condition
|
|
138
|
+
await userStatePart.waitUntilPresent();
|
|
139
|
+
|
|
140
|
+
// Wait for a specific property to be present
|
|
141
|
+
await userStatePart.waitUntilPresent(state => state.username);
|
|
142
|
+
|
|
143
|
+
// Setup initial state with async operations
|
|
144
|
+
await userStatePart.stateSetup(async (statePart) => {
|
|
145
|
+
// Perform async initialization
|
|
146
|
+
const userData = await fetchUserData();
|
|
147
|
+
return { ...statePart.getState(), ...userData };
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Defer notification to end of call stack
|
|
151
|
+
userStatePart.notifyChangeCumulative();
|
|
96
152
|
```
|
|
97
153
|
|
|
98
|
-
### Persistent State
|
|
154
|
+
### Persistent State with WebStore
|
|
99
155
|
|
|
100
|
-
`Smartstate` supports
|
|
156
|
+
`Smartstate` supports persistent states using WebStore (IndexedDB-based storage), allowing you to maintain state across sessions:
|
|
101
157
|
|
|
102
158
|
```typescript
|
|
103
|
-
const settingsStatePart = await myAppSmartState.getStatePart<
|
|
159
|
+
const settingsStatePart = await myAppSmartState.getStatePart<ISettingsState>(
|
|
104
160
|
AppStateParts.SettingsState,
|
|
105
161
|
{ theme: 'light' }, // Initial state
|
|
106
162
|
'persistent' // Mode
|
|
107
163
|
);
|
|
164
|
+
|
|
165
|
+
// Note: init() is called automatically for persistent mode
|
|
108
166
|
```
|
|
109
167
|
|
|
110
|
-
|
|
168
|
+
Persistent state automatically:
|
|
169
|
+
- Saves state changes to IndexedDB
|
|
170
|
+
- Restores state on application restart
|
|
171
|
+
- Manages storage with configurable database and store names
|
|
172
|
+
|
|
173
|
+
### Performance Optimization
|
|
174
|
+
|
|
175
|
+
`Smartstate` includes built-in performance optimizations:
|
|
176
|
+
|
|
177
|
+
- **State Change Detection**: Detects actual state changes to prevent unnecessary notifications when state values haven't truly changed
|
|
178
|
+
- **Cumulative Notifications**: Batch multiple state changes into a single notification using `notifyChangeCumulative()`
|
|
179
|
+
- **Selective Subscriptions**: Use selectors to subscribe only to specific state properties
|
|
180
|
+
|
|
181
|
+
### RxJS Integration
|
|
182
|
+
|
|
183
|
+
`Smartstate` leverages RxJS for reactive state management:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// State is exposed as an RxJS Subject
|
|
187
|
+
const stateObservable = userStatePart.select();
|
|
188
|
+
|
|
189
|
+
// Automatically starts with current state value
|
|
190
|
+
stateObservable.subscribe((state) => {
|
|
191
|
+
console.log('Current state:', state);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Use selectors for specific properties
|
|
195
|
+
userStatePart.select(state => state.username)
|
|
196
|
+
.pipe(
|
|
197
|
+
distinctUntilChanged(),
|
|
198
|
+
filter(username => username !== undefined)
|
|
199
|
+
)
|
|
200
|
+
.subscribe(username => {
|
|
201
|
+
console.log('Username changed:', username);
|
|
202
|
+
});
|
|
203
|
+
```
|
|
111
204
|
|
|
112
205
|
### Comprehensive Usage
|
|
113
206
|
|
|
114
207
|
Putting it all together, `@push.rocks/smartstate` offers a flexible and powerful pattern for managing application state. By modularizing state parts, subscribing to state changes, and controlling state modifications through actions, developers can maintain a clean and scalable architecture. Combining these strategies with persistent states unlocks the full potential for creating dynamic and user-friendly applications.
|
|
115
208
|
|
|
116
|
-
|
|
209
|
+
Key features:
|
|
210
|
+
- **Type-safe state management** with full TypeScript support
|
|
211
|
+
- **Reactive state updates** using RxJS observables
|
|
212
|
+
- **Persistent state** with IndexedDB storage
|
|
213
|
+
- **Performance optimized** with state hash detection
|
|
214
|
+
- **Modular architecture** with separate state parts
|
|
215
|
+
- **Action-based updates** for predictable state modifications
|
|
117
216
|
|
|
118
217
|
For more complex scenarios, consider combining multiple state parts, creating hierarchical state structures, and integrating with other state management solutions as needed. With `@push.rocks/smartstate`, the possibilities are vast, empowering you to tailor the state management approach to fit the unique requirements of your project.
|
|
119
218
|
|
|
@@ -14,7 +14,7 @@ export class StateAction<TStateType, TActionPayloadType> {
|
|
|
14
14
|
public actionDef: IActionDef<TStateType, TActionPayloadType>
|
|
15
15
|
) {}
|
|
16
16
|
|
|
17
|
-
public trigger(payload: TActionPayloadType) {
|
|
18
|
-
this.statePartRef.dispatchAction(this, payload);
|
|
17
|
+
public trigger(payload: TActionPayloadType): Promise<TStateType> {
|
|
18
|
+
return this.statePartRef.dispatchAction(this, payload);
|
|
19
19
|
}
|
|
20
20
|
}
|