cogsbox-state 0.5.467 → 0.5.469
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 +60 -309
- package/dist/CogsState.d.ts +62 -68
- package/dist/CogsState.d.ts.map +1 -1
- package/dist/CogsState.jsx +954 -1004
- package/dist/CogsState.jsx.map +1 -1
- package/dist/Components.d.ts +10 -0
- package/dist/Components.d.ts.map +1 -1
- package/dist/Components.jsx +141 -118
- package/dist/Components.jsx.map +1 -1
- package/dist/store.d.ts +2 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +154 -213
- package/src/Components.tsx +55 -3
- package/src/store.ts +2 -2
package/README.md
CHANGED
|
@@ -46,9 +46,9 @@ function UserComponent() {
|
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
48
|
<div>
|
|
49
|
-
<p>Name: {user.name
|
|
50
|
-
<p>Counter: {user.stats.counter
|
|
51
|
-
<button onClick={() => user.stats.counter
|
|
49
|
+
<p>Name: {user.name.$get()}</p>
|
|
50
|
+
<p>Counter: {user.stats.counter.$get()}</p>
|
|
51
|
+
<button onClick={() => user.stats.counter.$update(prev => prev + 1)}>
|
|
52
52
|
Increment Counter
|
|
53
53
|
</button>
|
|
54
54
|
</div>
|
|
@@ -60,7 +60,7 @@ function TodoComponent() {
|
|
|
60
60
|
|
|
61
61
|
return (
|
|
62
62
|
<div>
|
|
63
|
-
<p>Todo count: {todos
|
|
63
|
+
<p>Todo count: {todos.$get().length}</p>
|
|
64
64
|
<button onClick={() => todos.insert({ id: Date.now(), text: 'New todo', done: false })}>
|
|
65
65
|
Add Todo
|
|
66
66
|
</button>
|
|
@@ -77,16 +77,16 @@ Every state property gets these core methods:
|
|
|
77
77
|
|
|
78
78
|
#### Primitives (strings, numbers, booleans)
|
|
79
79
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
80
|
+
- `.$get()` - read values reactively
|
|
81
|
+
- `.$update()` - set values
|
|
82
|
+
- `.$toggle()` - flip booleans
|
|
83
83
|
- `.$get()` - non-reactive read (signals)
|
|
84
84
|
- `.$derive()` - computed signals
|
|
85
85
|
|
|
86
86
|
#### Objects
|
|
87
87
|
|
|
88
88
|
- All primitive methods plus access to nested properties
|
|
89
|
-
-
|
|
89
|
+
- `.$update()` can do partial updates
|
|
90
90
|
|
|
91
91
|
#### Arrays
|
|
92
92
|
|
|
@@ -101,19 +101,19 @@ const todos = useCogsState('todos');
|
|
|
101
101
|
const settings = useCogsState('settings');
|
|
102
102
|
|
|
103
103
|
// Reactive reads (triggers re-renders)
|
|
104
|
-
const userName = user.name
|
|
105
|
-
const allTodos = todos
|
|
106
|
-
const isDarkMode = settings.darkMode
|
|
104
|
+
const userName = user.name.$get();
|
|
105
|
+
const allTodos = todos.$get();
|
|
106
|
+
const isDarkMode = settings.darkMode.$get();
|
|
107
107
|
|
|
108
108
|
// Access nested properties
|
|
109
|
-
const counterValue = user.stats.counter
|
|
110
|
-
const firstTodo = todos
|
|
109
|
+
const counterValue = user.stats.counter.$get();
|
|
110
|
+
const firstTodo = todos.$index(0)?.$get();
|
|
111
111
|
|
|
112
112
|
// Non-reactive reads (no re-renders, for signals)
|
|
113
|
-
const userNameStatic = user.name
|
|
113
|
+
const userNameStatic = user.name.$$get();
|
|
114
114
|
|
|
115
115
|
// Computed signals (transforms value without re-renders)
|
|
116
|
-
const todoCount = todos
|
|
116
|
+
const todoCount = todos.$$derive((todos) => todos.length);
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
### Updating State
|
|
@@ -124,17 +124,17 @@ const settings = useCogsState('settings');
|
|
|
124
124
|
const todos = useCogsState('todos');
|
|
125
125
|
|
|
126
126
|
// Direct updates
|
|
127
|
-
user.name
|
|
128
|
-
settings.darkMode
|
|
127
|
+
user.name.$update('Jane');
|
|
128
|
+
settings.darkMode.$toggle();
|
|
129
129
|
|
|
130
130
|
// Functional updates
|
|
131
|
-
user.stats.counter
|
|
131
|
+
user.stats.counter.$update((prev) => prev + 1);
|
|
132
132
|
|
|
133
133
|
// Object updates
|
|
134
|
-
user
|
|
134
|
+
user.$update((prev) => ({ ...prev, name: 'Jane', age: 30 }));
|
|
135
135
|
|
|
136
136
|
// Deep nested updates
|
|
137
|
-
todos
|
|
137
|
+
todos.$index(0).text.$update('Updated todo text');
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
## Working with Arrays
|
|
@@ -147,20 +147,20 @@ Arrays are first-class citizens with powerful built-in operations:
|
|
|
147
147
|
const todos = useCogsState('todos');
|
|
148
148
|
|
|
149
149
|
// Add items
|
|
150
|
-
todos
|
|
151
|
-
todos
|
|
150
|
+
todos.$insert({ id: 'uuid', text: 'New todo', done: false });
|
|
151
|
+
todos.$insert(({ uuid }) => ({
|
|
152
152
|
id: uuid,
|
|
153
153
|
text: 'Auto-generated ID',
|
|
154
154
|
done: false,
|
|
155
155
|
}));
|
|
156
156
|
|
|
157
157
|
// Remove items
|
|
158
|
-
todos
|
|
159
|
-
todos
|
|
158
|
+
todos.$cut(2); // Remove at index 2
|
|
159
|
+
todos.$cutSelected(); // Remove currently selected item
|
|
160
160
|
|
|
161
161
|
// Access items
|
|
162
|
-
const firstTodo = todos
|
|
163
|
-
const lastTodo = todos
|
|
162
|
+
const firstTodo = todos.$index(0);
|
|
163
|
+
const lastTodo = todos.$last();
|
|
164
164
|
```
|
|
165
165
|
|
|
166
166
|
### Array Iteration and Rendering
|
|
@@ -171,12 +171,12 @@ const lastTodo = todos.last();
|
|
|
171
171
|
const todos = useCogsState('todos');
|
|
172
172
|
|
|
173
173
|
// Returns transformed array, each item is a full state object
|
|
174
|
-
const todoElements = todos
|
|
174
|
+
const todoElements = todos.$stateMap((todoState, index, arrayState) => (
|
|
175
175
|
<TodoItem
|
|
176
|
-
key={todoState.id
|
|
176
|
+
key={todoState.id.$get()}
|
|
177
177
|
todo={todoState}
|
|
178
|
-
onToggle={() => todoState.done
|
|
179
|
-
onDelete={() => arrayState
|
|
178
|
+
onToggle={() => todoState.done.$toggle()}
|
|
179
|
+
onDelete={() => arrayState.$cut(index)}
|
|
180
180
|
/>
|
|
181
181
|
));
|
|
182
182
|
```
|
|
@@ -187,10 +187,10 @@ const todoElements = todos.stateMap((todoState, index, arrayState) => (
|
|
|
187
187
|
const todos = useCogsState('todos');
|
|
188
188
|
|
|
189
189
|
// Renders directly in place with automatic key management
|
|
190
|
-
{todos
|
|
191
|
-
<div key={todoState.id
|
|
192
|
-
<span>{todoState.text
|
|
193
|
-
<button onClick={() => todoState.done
|
|
190
|
+
{todos.$stateList((todoState, index, arrayState) => (
|
|
191
|
+
<div key={todoState.id.$get()}>
|
|
192
|
+
<span>{todoState.text.$get()}</span>
|
|
193
|
+
<button onClick={() => todoState.done.$toggle()}>Toggle</button>
|
|
194
194
|
<button onClick={() => arrayState.cut(index)}>Delete</button>
|
|
195
195
|
</div>
|
|
196
196
|
))}
|
|
@@ -215,16 +215,16 @@ const todos = useCogsState('todos');
|
|
|
215
215
|
const todos = useCogsState('todos');
|
|
216
216
|
|
|
217
217
|
// Filter items (returns new state object with filtered view)
|
|
218
|
-
const completedTodos = todos
|
|
219
|
-
const incompleteTodos = todos
|
|
218
|
+
const completedTodos = todos.$stateFilter((todo) => todo.done);
|
|
219
|
+
const incompleteTodos = todos.$stateFilter((todo) => !todo.done);
|
|
220
220
|
|
|
221
221
|
// Sort items (returns new state object with sorted view)
|
|
222
|
-
const sortedTodos = todos
|
|
222
|
+
const sortedTodos = todos.$stateSort((a, b) => a.text.localeCompare(b.text));
|
|
223
223
|
|
|
224
224
|
// Chain operations
|
|
225
225
|
const sortedCompletedTodos = todos
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
.$stateFilter((todo) => todo.done)
|
|
227
|
+
.$stateSort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
|
|
228
228
|
```
|
|
229
229
|
|
|
230
230
|
#### Finding and Searching
|
|
@@ -233,13 +233,13 @@ const sortedCompletedTodos = todos
|
|
|
233
233
|
const todos = useCogsState('todos');
|
|
234
234
|
|
|
235
235
|
// Find by property value
|
|
236
|
-
const todoById = todos
|
|
236
|
+
const todoById = todos.$findWith('id', 'some-id');
|
|
237
237
|
if (todoById) {
|
|
238
|
-
todoById.text
|
|
238
|
+
todoById.text.$update('Updated text');
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
// Find with custom function
|
|
242
|
-
const firstIncompleteTodo = todos
|
|
242
|
+
const firstIncompleteTodo = todos.$stateFind((todo) => !todo.done);
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
#### Unique Operations
|
|
@@ -248,7 +248,7 @@ const firstIncompleteTodo = todos.stateFind((todo) => !todo.done);
|
|
|
248
248
|
const todos = useCogsState('todos');
|
|
249
249
|
|
|
250
250
|
// Insert only if unique (prevents duplicates)
|
|
251
|
-
todos
|
|
251
|
+
todos.$uniqueInsert(
|
|
252
252
|
{ id: 'new-id', text: 'New todo', done: false },
|
|
253
253
|
['id'], // Fields to check for uniqueness
|
|
254
254
|
(existingItem) => {
|
|
@@ -258,7 +258,7 @@ todos.uniqueInsert(
|
|
|
258
258
|
);
|
|
259
259
|
|
|
260
260
|
// Toggle presence (insert if missing, remove if present)
|
|
261
|
-
todos
|
|
261
|
+
todos.$toggleByValue('some-id');
|
|
262
262
|
```
|
|
263
263
|
|
|
264
264
|
#### Selection Management
|
|
@@ -267,18 +267,18 @@ todos.toggleByValue('some-id');
|
|
|
267
267
|
const todos = useCogsState('todos');
|
|
268
268
|
|
|
269
269
|
// Built-in selection tracking
|
|
270
|
-
const selectedTodo = todos
|
|
271
|
-
const selectedIndex = todos
|
|
270
|
+
const selectedTodo = todos.$getSelected();
|
|
271
|
+
const selectedIndex = todos.$getSelectedIndex();
|
|
272
272
|
|
|
273
273
|
// Set selection on individual items
|
|
274
|
-
todos
|
|
275
|
-
todos
|
|
274
|
+
todos.$index(0).$setSelected(true);
|
|
275
|
+
todos.$index(0).$toggleSelected();
|
|
276
276
|
|
|
277
277
|
// Clear all selections
|
|
278
|
-
todos
|
|
278
|
+
todos.$clearSelected();
|
|
279
279
|
|
|
280
280
|
// Check if item is selected
|
|
281
|
-
const isSelected = todos
|
|
281
|
+
const isSelected = todos.$index(0).isSelected;
|
|
282
282
|
```
|
|
283
283
|
|
|
284
284
|
<!-- ### Virtualization for Large Lists
|
|
@@ -302,7 +302,7 @@ function MessageList() {
|
|
|
302
302
|
<div style={virtualizerProps.inner.style}>
|
|
303
303
|
<div style={virtualizerProps.list.style}>
|
|
304
304
|
{virtualState.stateList((messageState, index) => (
|
|
305
|
-
<MessageItem key={messageState.id
|
|
305
|
+
<MessageItem key={messageState.id.$get()} message={messageState} />
|
|
306
306
|
))}
|
|
307
307
|
</div>
|
|
308
308
|
</div>
|
|
@@ -311,37 +311,6 @@ function MessageList() {
|
|
|
311
311
|
}
|
|
312
312
|
``` -->
|
|
313
313
|
|
|
314
|
-
### Streaming for Real-time Data
|
|
315
|
-
|
|
316
|
-
```typescript
|
|
317
|
-
const messages = useCogsState('messages');
|
|
318
|
-
|
|
319
|
-
// Create a stream for efficient batch operations
|
|
320
|
-
const messageStream = messages.stream({
|
|
321
|
-
bufferSize: 100, // Buffer size before auto-flush
|
|
322
|
-
flushInterval: 100, // Auto-flush interval (ms)
|
|
323
|
-
bufferStrategy: 'sliding', // 'sliding' | 'dropping' | 'accumulate'
|
|
324
|
-
store: (buffer) => buffer, // Transform buffered items before insertion
|
|
325
|
-
onFlush: (buffer) => console.log('Flushed', buffer.length, 'items'),
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
// Write individual items
|
|
329
|
-
messageStream.write(newMessage);
|
|
330
|
-
|
|
331
|
-
// Write multiple items
|
|
332
|
-
messageStream.writeMany([msg1, msg2, msg3]);
|
|
333
|
-
|
|
334
|
-
// Manual flush
|
|
335
|
-
messageStream.flush();
|
|
336
|
-
|
|
337
|
-
// Pause/resume
|
|
338
|
-
messageStream.pause();
|
|
339
|
-
messageStream.resume();
|
|
340
|
-
|
|
341
|
-
// Close stream
|
|
342
|
-
messageStream.close();
|
|
343
|
-
```
|
|
344
|
-
|
|
345
314
|
## Reactivity Control
|
|
346
315
|
|
|
347
316
|
Cogsbox offers different reactivity modes for performance optimization:
|
|
@@ -409,14 +378,14 @@ function PerformantComponent() {
|
|
|
409
378
|
{todos.$stateMap((todo, index) => (
|
|
410
379
|
<div key={todo.id.$get()}>
|
|
411
380
|
<span>{todo.text.$get()}</span>
|
|
412
|
-
<button onClick={() => todo.done
|
|
381
|
+
<button onClick={() => todo.done.$toggle()}>Toggle</button>
|
|
413
382
|
</div>
|
|
414
383
|
))}
|
|
415
384
|
|
|
416
385
|
{/* Wrap with formElement for isolated reactivity */}
|
|
417
386
|
{user.stats.counter.formElement((obj) => (
|
|
418
|
-
<button onClick={() => obj
|
|
419
|
-
Increment: {obj
|
|
387
|
+
<button onClick={() => obj.$update(prev => prev + 1)}>
|
|
388
|
+
Increment: {obj.$get()}
|
|
420
389
|
</button>
|
|
421
390
|
))}
|
|
422
391
|
</div>
|
|
@@ -474,246 +443,28 @@ function UserForm() {
|
|
|
474
443
|
))}
|
|
475
444
|
|
|
476
445
|
{/* Custom debounce time */}
|
|
477
|
-
{userForm.email.formElement(({ inputProps, get, update }) => (
|
|
446
|
+
{userForm.email.formElement(({ inputProps, $get, $update }) => (
|
|
478
447
|
<>
|
|
479
448
|
<label>Email</label>
|
|
480
449
|
<input {...inputProps} />
|
|
481
|
-
<small>Current: {get()}</small>
|
|
450
|
+
<small>Current: {$get()}</small>
|
|
482
451
|
</>
|
|
483
452
|
), { debounceTime: 500 })}
|
|
484
453
|
|
|
485
454
|
{/* Custom form control */}
|
|
486
|
-
{userForm.age.formElement(({ get, update }) => (
|
|
455
|
+
{userForm.age.formElement(({ $get, $update }) => (
|
|
487
456
|
<>
|
|
488
457
|
<label>Age</label>
|
|
489
458
|
<input
|
|
490
459
|
type="number"
|
|
491
|
-
value={get()}
|
|
492
|
-
onChange={e => update(parseInt(e.target.value))}
|
|
460
|
+
value={$get()}
|
|
461
|
+
onChange={e => $update(parseInt(e.target.value))}
|
|
493
462
|
/>
|
|
494
463
|
</>
|
|
495
464
|
))}
|
|
496
465
|
|
|
497
|
-
<button onClick={() => {
|
|
498
|
-
if (userForm.validateZodSchema()) {
|
|
499
|
-
console.log('Valid!', userForm.get());
|
|
500
|
-
}
|
|
501
|
-
}}>
|
|
502
|
-
Submit
|
|
503
|
-
</button>
|
|
504
|
-
</form>
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
## Advanced Features
|
|
510
|
-
|
|
511
|
-
### Server Synchronization
|
|
512
|
-
|
|
513
|
-
```typescript
|
|
514
|
-
const { useCogsState } = createCogsState({
|
|
515
|
-
userProfile: {
|
|
516
|
-
initialState: { name: "", email: "" },
|
|
517
|
-
sync: {
|
|
518
|
-
action: async (state) => {
|
|
519
|
-
const response = await fetch('/api/user', {
|
|
520
|
-
method: 'PUT',
|
|
521
|
-
body: JSON.stringify(state)
|
|
522
|
-
});
|
|
523
|
-
return response.ok
|
|
524
|
-
? { success: true, data: await response.json() }
|
|
525
|
-
: { success: false, error: 'Failed to save' };
|
|
526
|
-
},
|
|
527
|
-
onSuccess: (data) => console.log('Saved!', data),
|
|
528
|
-
onError: (error) => console.error('Save failed:', error)
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
function UserProfile() {
|
|
534
|
-
const userProfile = useCogsState('userProfile');
|
|
535
|
-
|
|
536
|
-
return (
|
|
537
|
-
<div>
|
|
538
|
-
<div>Status: {userProfile.getStatus()}</div> {/* 'fresh' | 'dirty' | 'synced' | 'restored' */}
|
|
539
|
-
<input
|
|
540
|
-
value={userProfile.name.get()}
|
|
541
|
-
onChange={e => userProfile.name.update(e.target.value)}
|
|
542
|
-
/>
|
|
543
|
-
<button onClick={() => userProfile.sync()}>Save to Server</button>
|
|
544
|
-
</div>
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
### Local Storage Integration
|
|
550
|
-
|
|
551
|
-
```typescript
|
|
552
|
-
const { useCogsState } = createCogsState({
|
|
553
|
-
userPrefs: {
|
|
554
|
-
initialState: { theme: 'dark', language: 'en' },
|
|
555
|
-
localStorage: {
|
|
556
|
-
key: 'user-preferences',
|
|
557
|
-
onChange: (state) => console.log('Saved to localStorage:', state),
|
|
558
|
-
},
|
|
559
|
-
},
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
function PreferencesComponent() {
|
|
563
|
-
const userPrefs = useCogsState('userPrefs');
|
|
564
466
|
|
|
565
|
-
|
|
566
|
-
<div>
|
|
567
|
-
<select
|
|
568
|
-
value={userPrefs.theme.get()}
|
|
569
|
-
onChange={e => userPrefs.theme.update(e.target.value)}
|
|
570
|
-
>
|
|
571
|
-
<option value="dark">Dark</option>
|
|
572
|
-
<option value="light">Light</option>
|
|
573
|
-
</select>
|
|
574
|
-
</div>
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
### State Status and History
|
|
580
|
-
|
|
581
|
-
```typescript
|
|
582
|
-
const user = useCogsState('user');
|
|
583
|
-
|
|
584
|
-
// Check what changed from initial state
|
|
585
|
-
const differences = user.getDifferences();
|
|
586
|
-
|
|
587
|
-
// Get current status
|
|
588
|
-
const status = user.getStatus(); // 'fresh' | 'dirty' | 'synced' | 'restored'
|
|
589
|
-
|
|
590
|
-
// Revert to initial state
|
|
591
|
-
user.revertToInitialState();
|
|
592
|
-
|
|
593
|
-
// Update initial state (useful for findign diffs and server-synced data)
|
|
594
|
-
const newServerData = {
|
|
595
|
-
name: 'Jane Doe',
|
|
596
|
-
age: 31,
|
|
597
|
-
stats: { counter: 100, lastUpdated: new Date() },
|
|
598
|
-
};
|
|
599
|
-
user.updateInitialState(newServerData);
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
### Component Isolation
|
|
603
|
-
|
|
604
|
-
```typescript
|
|
605
|
-
// Each component can have its own reactive settings
|
|
606
|
-
function ComponentA() {
|
|
607
|
-
const user = useCogsState('user', {
|
|
608
|
-
reactiveType: 'deps',
|
|
609
|
-
reactiveDeps: (state) => [state.name],
|
|
610
|
-
});
|
|
611
|
-
// Only re-renders when user.name changes
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
function ComponentB() {
|
|
615
|
-
const user = useCogsState('user', {
|
|
616
|
-
reactiveType: 'all',
|
|
617
|
-
});
|
|
618
|
-
// Re-renders on any change to 'user' state
|
|
619
|
-
}
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
## Performance Tips
|
|
623
|
-
|
|
624
|
-
1. **Use signals for high-frequency updates**: `.$get()` and `.$derive()` don't trigger React re-renders
|
|
625
|
-
2. **Use `reactiveType: 'none'` with signals**: Maximum performance for signal-heavy components
|
|
626
|
-
3. **Use virtualization for large lists**: `useVirtualView()` handles thousands of items efficiently
|
|
627
|
-
4. **Use streaming for real-time data**: Batch operations with `stream()` for better performance
|
|
628
|
-
5. **Chain filter/sort operations**: `stateFilter().stateSort()` creates efficient views
|
|
629
|
-
6. **Use `formElement` for forms**: Automatic debouncing and validation handling
|
|
630
|
-
|
|
631
|
-
## Common Patterns
|
|
632
|
-
|
|
633
|
-
### Master-Detail Interface
|
|
634
|
-
|
|
635
|
-
```typescript
|
|
636
|
-
function TodoApp() {
|
|
637
|
-
const todos = useCogsState('todos');
|
|
638
|
-
const selectedTodo = todos.getSelected();
|
|
639
|
-
|
|
640
|
-
return (
|
|
641
|
-
<div className="flex">
|
|
642
|
-
<div className="list">
|
|
643
|
-
{todos.stateList((todo, index) => (
|
|
644
|
-
<div
|
|
645
|
-
key={todo.id.get()}
|
|
646
|
-
className={todo.isSelected ? 'selected' : ''}
|
|
647
|
-
onClick={() => todo.toggleSelected()}
|
|
648
|
-
>
|
|
649
|
-
{todo.text.get()}
|
|
650
|
-
</div>
|
|
651
|
-
))}
|
|
652
|
-
</div>
|
|
653
|
-
|
|
654
|
-
<div className="detail">
|
|
655
|
-
{selectedTodo ? (
|
|
656
|
-
<TodoDetail todo={selectedTodo} />
|
|
657
|
-
) : (
|
|
658
|
-
<p>Select a todo</p>
|
|
659
|
-
)}
|
|
660
|
-
</div>
|
|
661
|
-
</div>
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
### Real-time Chat with Virtualization
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
function ChatRoom() {
|
|
670
|
-
const messages = useCogsState('messages', { reactiveType: 'none' });
|
|
671
|
-
|
|
672
|
-
const { virtualState, virtualizerProps, scrollToBottom } =
|
|
673
|
-
messages.useVirtualView({
|
|
674
|
-
itemHeight: 65,
|
|
675
|
-
overscan: 10,
|
|
676
|
-
stickToBottom: true,
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
return (
|
|
680
|
-
<div {...virtualizerProps.outer} className="chat-container">
|
|
681
|
-
<div style={virtualizerProps.inner.style}>
|
|
682
|
-
<div style={virtualizerProps.list.style}>
|
|
683
|
-
{virtualState.stateList((message) => (
|
|
684
|
-
<MessageItem key={message.id.$get()} message={message} />
|
|
685
|
-
))}
|
|
686
|
-
</div>
|
|
687
|
-
</div>
|
|
688
|
-
</div>
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
### Multiple State Slices in One Component
|
|
694
|
-
|
|
695
|
-
```typescript
|
|
696
|
-
function Dashboard() {
|
|
697
|
-
const user = useCogsState('user');
|
|
698
|
-
const todos = useCogsState('todos');
|
|
699
|
-
const settings = useCogsState('settings');
|
|
700
|
-
|
|
701
|
-
return (
|
|
702
|
-
<div>
|
|
703
|
-
<header>
|
|
704
|
-
<h1>Welcome, {user.name.get()}</h1>
|
|
705
|
-
<button onClick={() => settings.darkMode.toggle()}>
|
|
706
|
-
Toggle Theme
|
|
707
|
-
</button>
|
|
708
|
-
</header>
|
|
709
|
-
|
|
710
|
-
<main>
|
|
711
|
-
<p>You have {todos.get().length} todos</p>
|
|
712
|
-
<p>Counter: {user.stats.counter.get()}</p>
|
|
713
|
-
</main>
|
|
714
|
-
</div>
|
|
467
|
+
</form>
|
|
715
468
|
);
|
|
716
469
|
}
|
|
717
470
|
```
|
|
718
|
-
|
|
719
|
-
This library provides a unique approach to React state management by creating a proxy that mirrors your data structure while adding powerful methods for manipulation, rendering, and performance optimization.
|