@oimdb/react 1.1.1 → 1.2.0

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 CHANGED
@@ -8,7 +8,8 @@ React integration for OIMDB - Hooks for selection and subscription with reactive
8
8
 
9
9
  ## Features
10
10
 
11
- - **Reactive Integration**: Hooks work with `OIMReactiveCollection` and `OIMReactiveIndex` from `@oimdb/core`
11
+ - **Reactive Integration**: Hooks work with `OIMReactiveCollection` and reactive indexes from `@oimdb/core`
12
+ - **Index Type Support**: Separate hooks for SetBased indexes (return `Set<TPk>`) and ArrayBased indexes (return `TPk[]`)
12
13
  - **Automatic Subscription**: Uses `useSyncExternalStore` for optimal React 18+ performance
13
14
  - **Event Coalescing**: Leverages OIMDB's built-in event coalescing for efficient updates
14
15
  - **Type Safety**: Full TypeScript support with advanced generic type inference
@@ -26,16 +27,28 @@ npm install @oimdb/react @oimdb/core
26
27
  ### Basic Setup
27
28
 
28
29
  ```typescript
29
- import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
30
+ import {
31
+ OIMEventQueue,
32
+ OIMRICollection,
33
+ OIMReactiveIndexManualSetBased,
34
+ OIMReactiveIndexManualArrayBased
35
+ } from '@oimdb/core';
30
36
  import {
31
37
  useSelectEntitiesByPks,
32
- useSelectEntitiesByIndexKey,
38
+ useSelectEntitiesByIndexKeySetBased,
39
+ useSelectEntitiesByIndexKeyArrayBased,
33
40
  useSelectEntityByPk
34
41
  } from '@oimdb/react';
35
42
 
36
43
  // Create event queue and reactive collections
37
44
  const queue = new OIMEventQueue();
38
- const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
45
+
46
+ // Choose index type based on your needs:
47
+ // - SetBased: for frequent add/remove operations, order doesn't matter
48
+ // - ArrayBased: for full replacements or when order/sorting matters
49
+ const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
50
+ const deckCardsIndex = new OIMReactiveIndexManualArrayBased<string, string>(queue);
51
+
39
52
  const usersCollection = new OIMRICollection(queue, {
40
53
  collectionOpts: { selectPk: (user: User) => user.id },
41
54
  indexes: { byTeam: userTeamIndex },
@@ -79,14 +92,27 @@ function UserList({ userIds }: { userIds: string[] }) {
79
92
 
80
93
  ### Index-based Selection
81
94
 
95
+ OIMDB provides separate hooks for SetBased and ArrayBased indexes:
96
+
97
+ #### SetBased Indexes (returns Set)
98
+
82
99
  ```typescript
100
+ import { useSelectEntitiesByIndexKeySetBased, useSelectPksByIndexKeySetBased } from '@oimdb/react';
101
+
83
102
  function TeamMembers({ teamId }: { teamId: string }) {
84
- const teamUsers = useSelectEntitiesByIndexKey(
103
+ // For SetBased indexes, use SetBased hooks
104
+ const teamUsers = useSelectEntitiesByIndexKeySetBased(
85
105
  usersCollection,
86
- usersCollection.indexes.byTeam,
106
+ usersCollection.indexes.byTeam, // OIMReactiveIndexManualSetBased
87
107
  teamId
88
108
  );
89
109
 
110
+ // Or get just the PKs as Set
111
+ const teamUserIds = useSelectPksByIndexKeySetBased(
112
+ usersCollection.indexes.byTeam,
113
+ teamId
114
+ ); // Returns Set<string>
115
+
90
116
  return (
91
117
  <div>
92
118
  {teamUsers.map((user, index) => (
@@ -99,6 +125,37 @@ function TeamMembers({ teamId }: { teamId: string }) {
99
125
  }
100
126
  ```
101
127
 
128
+ #### ArrayBased Indexes (returns Array)
129
+
130
+ ```typescript
131
+ import { useSelectEntitiesByIndexKeyArrayBased, useSelectPksByIndexKeyArrayBased } from '@oimdb/react';
132
+
133
+ function DeckCards({ deckId }: { deckId: string }) {
134
+ // For ArrayBased indexes, use ArrayBased hooks
135
+ const cards = useSelectEntitiesByIndexKeyArrayBased(
136
+ cardsCollection,
137
+ cardsCollection.indexes.byDeck, // OIMReactiveIndexManualArrayBased
138
+ deckId
139
+ );
140
+
141
+ // Or get just the PKs as Array (preserves order)
142
+ const cardIds = useSelectPksByIndexKeyArrayBased(
143
+ cardsCollection.indexes.byDeck,
144
+ deckId
145
+ ); // Returns string[] (preserves order/sorting)
146
+
147
+ return (
148
+ <div>
149
+ {cards.map((card, index) => (
150
+ <div key={card?.id || index}>
151
+ {card ? `${index + 1}. ${card.name}` : 'Loading...'}
152
+ </div>
153
+ ))}
154
+ </div>
155
+ );
156
+ }
157
+ ```
158
+
102
159
  ## React Context Integration
103
160
 
104
161
  For applications with multiple collections, use the React Context pattern for centralized management:
@@ -125,7 +182,8 @@ interface Team {
125
182
  function createCollections() {
126
183
  const queue = new OIMEventQueue();
127
184
 
128
- const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
185
+ // Use SetBased for frequent add/remove operations
186
+ const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
129
187
  const usersCollection = new OIMRICollection(queue, {
130
188
  collectionOpts: { selectPk: (user: User) => user.id },
131
189
  indexes: { byTeam: userTeamIndex },
@@ -166,7 +224,7 @@ interface Team {
166
224
 
167
225
  // Create collections
168
226
  const queue = new OIMEventQueue();
169
- const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
227
+ const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
170
228
  const usersCollection = new OIMRICollection(queue, {
171
229
  collectionOpts: { selectPk: (user: User) => user.id },
172
230
  indexes: { byTeam: userTeamIndex },
@@ -200,8 +258,16 @@ function MyComponent() {
200
258
  For larger applications or when you need more control, create explicit type definitions similar to Redux's approach:
201
259
 
202
260
  ```typescript
203
- import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
204
- import type { TOIMPk, OIMIndex, OIMReactiveIndex } from '@oimdb/core';
261
+ import {
262
+ OIMEventQueue,
263
+ OIMRICollection,
264
+ OIMReactiveIndexManualSetBased
265
+ } from '@oimdb/core';
266
+ import type {
267
+ TOIMPk,
268
+ OIMIndexSetBased,
269
+ OIMReactiveIndexSetBased
270
+ } from '@oimdb/core';
205
271
 
206
272
  interface User {
207
273
  id: string;
@@ -220,8 +286,8 @@ type UserCollection = OIMRICollection<
220
286
  string,
221
287
  'byTeam',
222
288
  string,
223
- OIMIndex<string, string>,
224
- OIMReactiveIndex<string, string, OIMIndex<string, string>>
289
+ OIMIndexSetBased<string, string>,
290
+ OIMReactiveIndexSetBased<string, string, OIMIndexSetBased<string, string>>
225
291
  >;
226
292
 
227
293
  type TeamCollection = OIMRICollection<
@@ -229,8 +295,8 @@ type TeamCollection = OIMRICollection<
229
295
  string,
230
296
  never,
231
297
  never,
232
- OIMIndex<never, never>,
233
- OIMReactiveIndex<never, never, OIMIndex<never, never>>
298
+ OIMIndexSetBased<never, never>,
299
+ OIMReactiveIndexSetBased<never, never, OIMIndexSetBased<never, never>>
234
300
  >;
235
301
 
236
302
  // Define your collections dictionary type
@@ -242,7 +308,7 @@ interface AppCollections {
242
308
  // Factory function that returns properly typed collections
243
309
  function createCollections(): AppCollections {
244
310
  const queue = new OIMEventQueue();
245
- const userTeamIndex = new OIMReactiveIndexManual<string, string>(queue);
311
+ const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
246
312
 
247
313
  return {
248
314
  users: new OIMRICollection(queue, {
@@ -303,9 +369,10 @@ function UserDashboard() {
303
369
 
304
370
  // Use collections with hooks
305
371
  const allUsers = useSelectEntitiesByPks(users, []);
306
- const teamMembers = useSelectEntitiesByIndexKey(
372
+ // Use appropriate hook based on index type
373
+ const teamMembers = useSelectEntitiesByIndexKeySetBased(
307
374
  users,
308
- users.indexes.byTeam,
375
+ users.indexes.byTeam, // SetBased index
309
376
  'team1'
310
377
  );
311
378
 
@@ -365,51 +432,105 @@ Subscribes to multiple entities from a reactive collection.
365
432
  **Returns:**
366
433
  - `(TEntity | undefined)[]` - Array of entities (undefined for missing entities)
367
434
 
368
- ### `useSelectEntitiesByIndexKey(reactiveCollection, reactiveIndex, key)`
435
+ ### Index-based Selection Hooks
369
436
 
370
- Subscribes to entities indexed by a specific key.
437
+ OIMDB provides separate hooks for SetBased and ArrayBased indexes to ensure type safety and correct return types.
438
+
439
+ #### SetBased Index Hooks
440
+
441
+ ##### `useSelectEntitiesByIndexKeySetBased(reactiveCollection, reactiveIndex, key)`
442
+
443
+ Subscribes to entities indexed by a specific key from a SetBased index.
371
444
 
372
445
  **Parameters:**
373
446
  - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
374
- - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
447
+ - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
375
448
  - `key: TKey` - Index key to query
376
449
 
377
450
  **Returns:**
378
451
  - `(TEntity | undefined)[]` - Array of entities for the given index key
379
452
 
380
- ### `useSelectEntitiesByIndexKeys(reactiveCollection, reactiveIndex, keys)`
453
+ ##### `useSelectEntitiesByIndexKeysSetBased(reactiveCollection, reactiveIndex, keys)`
381
454
 
382
- Subscribes to entities indexed by multiple keys.
455
+ Subscribes to entities indexed by multiple keys from a SetBased index.
383
456
 
384
457
  **Parameters:**
385
458
  - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
386
- - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
459
+ - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
387
460
  - `keys: readonly TKey[]` - Array of index keys to query
388
461
 
389
462
  **Returns:**
390
463
  - `(TEntity | undefined)[]` - Array of entities for the given index keys
391
464
 
392
- ### `useSelectPksByIndexKey(reactiveIndex, key)`
465
+ ##### `useSelectPksByIndexKeySetBased(reactiveIndex, key)`
466
+
467
+ Subscribes to primary keys indexed by a specific key from a SetBased index.
468
+
469
+ **Parameters:**
470
+ - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
471
+ - `key: TKey` - Index key to query
472
+
473
+ **Returns:**
474
+ - `Set<TPk>` - Set of primary keys for the given index key
475
+
476
+ ##### `useSelectPksByIndexKeysSetBased(reactiveIndex, keys)`
477
+
478
+ Subscribes to primary keys indexed by multiple keys from a SetBased index.
479
+
480
+ **Parameters:**
481
+ - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
482
+ - `keys: readonly TKey[]` - Array of index keys to query
483
+
484
+ **Returns:**
485
+ - `Map<TKey, Set<TPk>>` - Map of index keys to their corresponding primary key Sets
486
+
487
+ #### ArrayBased Index Hooks
488
+
489
+ ##### `useSelectEntitiesByIndexKeyArrayBased(reactiveCollection, reactiveIndex, key)`
490
+
491
+ Subscribes to entities indexed by a specific key from an ArrayBased index.
492
+
493
+ **Parameters:**
494
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
495
+ - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
496
+ - `key: TKey` - Index key to query
497
+
498
+ **Returns:**
499
+ - `(TEntity | undefined)[]` - Array of entities for the given index key (preserves order)
500
+
501
+ ##### `useSelectEntitiesByIndexKeysArrayBased(reactiveCollection, reactiveIndex, keys)`
502
+
503
+ Subscribes to entities indexed by multiple keys from an ArrayBased index.
393
504
 
394
- Subscribes to primary keys indexed by a specific key.
505
+ **Parameters:**
506
+ - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
507
+ - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
508
+ - `keys: readonly TKey[]` - Array of index keys to query
509
+
510
+ **Returns:**
511
+ - `(TEntity | undefined)[]` - Array of entities for the given index keys (preserves order)
512
+
513
+ ##### `useSelectPksByIndexKeyArrayBased(reactiveIndex, key)`
514
+
515
+ Subscribes to primary keys indexed by a specific key from an ArrayBased index.
395
516
 
396
517
  **Parameters:**
397
- - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
518
+ - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
398
519
  - `key: TKey` - Index key to query
399
520
 
400
521
  **Returns:**
401
- - `TPk[]` - Array of primary keys for the given index key
522
+ - `TPk[]` - Array of primary keys for the given index key (preserves order/sorting)
402
523
 
403
- ### `useSelectPksByIndexKeys(reactiveIndex, keys)`
524
+ ##### `useSelectPksByIndexKeysArrayBased(reactiveIndex, keys)`
404
525
 
405
- Subscribes to primary keys indexed by multiple keys.
526
+ Subscribes to primary keys indexed by multiple keys from an ArrayBased index.
406
527
 
407
528
  **Parameters:**
408
- - `reactiveIndex: OIMReactiveIndex<TKey, TPk, TIndex>` - Reactive index instance
529
+ - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
409
530
  - `keys: readonly TKey[]` - Array of index keys to query
410
531
 
411
532
  **Returns:**
412
- - `Map<TKey, TPk[]>` - Map of index keys to their corresponding primary keys
533
+ - `Map<TKey, TPk[]>` - Map of index keys to their corresponding primary key arrays (preserves order)
413
534
 
414
535
  ## Context API Reference
415
536
 
@@ -457,7 +578,19 @@ The hooks work directly with OIMDB reactive objects:
457
578
  ```typescript
458
579
  // Use reactive collections and indexes directly
459
580
  const user = useSelectEntityByPk(reactiveCollection, 'user123');
460
- const posts = useSelectEntitiesByIndexKey(reactiveCollection, reactiveIndex, 'tech');
581
+
582
+ // Use appropriate hook based on index type
583
+ const posts = useSelectEntitiesByIndexKeySetBased(
584
+ reactiveCollection,
585
+ reactiveIndexSetBased, // SetBased index
586
+ 'tech'
587
+ );
588
+
589
+ const orderedCards = useSelectEntitiesByIndexKeyArrayBased(
590
+ reactiveCollection,
591
+ reactiveIndexArrayBased, // ArrayBased index
592
+ 'deck1'
593
+ );
461
594
  ```
462
595
 
463
596
  ### Event Subscription
@@ -469,6 +602,15 @@ Hooks automatically subscribe to OIMDB reactive events using `useSyncExternalSto
469
602
  - **Optimized subscriptions**: Subscribe only to specific keys for efficient updates
470
603
  - **Automatic cleanup**: Unsubscribe when component unmounts
471
604
 
605
+ ### Index Type Selection
606
+
607
+ When working with indexes, choose the appropriate hook based on your index type:
608
+
609
+ - **SetBased indexes** (`OIMReactiveIndexManualSetBased`): Use `*SetBased` hooks (e.g., `useSelectPksByIndexKeySetBased`) - returns `Set<TPk>`
610
+ - **ArrayBased indexes** (`OIMReactiveIndexManualArrayBased`): Use `*ArrayBased` hooks (e.g., `useSelectPksByIndexKeyArrayBased`) - returns `TPk[]` (preserves order)
611
+
612
+ This ensures type safety and correct return types. TypeScript will enforce the correct hook usage based on your index type.
613
+
472
614
  ### Performance
473
615
 
474
616
  - **React 18+ Integration**: Uses `useSyncExternalStore` for optimal performance
@@ -483,8 +625,18 @@ Hooks automatically subscribe to OIMDB reactive events using `useSyncExternalSto
483
625
 
484
626
  ```typescript
485
627
  import React from 'react';
486
- import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
487
- import { useSelectEntityByPk, useSelectEntitiesByPks, useSelectEntitiesByIndexKey } from '@oimdb/react';
628
+ import {
629
+ OIMEventQueue,
630
+ OIMRICollection,
631
+ OIMReactiveIndexManualSetBased,
632
+ OIMReactiveIndexManualArrayBased
633
+ } from '@oimdb/core';
634
+ import {
635
+ useSelectEntityByPk,
636
+ useSelectEntitiesByPks,
637
+ useSelectEntitiesByIndexKeySetBased,
638
+ useSelectEntitiesByIndexKeyArrayBased
639
+ } from '@oimdb/react';
488
640
 
489
641
  interface User {
490
642
  id: string;
@@ -496,7 +648,8 @@ interface User {
496
648
  // Setup
497
649
  function createUserCollection() {
498
650
  const queue = new OIMEventQueue();
499
- const teamIndex = new OIMReactiveIndexManual<string, string>(queue);
651
+ // Use SetBased for frequent add/remove operations
652
+ const teamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
500
653
 
501
654
  return new OIMRICollection(queue, {
502
655
  collectionOpts: { selectPk: (user: User) => user.id },
@@ -516,9 +669,10 @@ function UserProfile({ userId }: { userId: string }) {
516
669
  }
517
670
 
518
671
  function TeamDashboard({ teamId }: { teamId: string }) {
519
- const teamMembers = useSelectEntitiesByIndexKey(
672
+ // Use SetBased hook for SetBased index
673
+ const teamMembers = useSelectEntitiesByIndexKeySetBased(
520
674
  usersCollection,
521
- usersCollection.indexes.byTeam,
675
+ usersCollection.indexes.byTeam, // OIMReactiveIndexManualSetBased
522
676
  teamId
523
677
  );
524
678
 
@@ -526,7 +680,7 @@ function TeamDashboard({ teamId }: { teamId: string }) {
526
680
  <div>
527
681
  <h3>Team Members ({teamMembers.length})</h3>
528
682
  {teamMembers.map(user => (
529
- <div key={user.id}>{user.name}</div>
683
+ <div key={user?.id}>{user?.name}</div>
530
684
  ))}
531
685
  </div>
532
686
  );
@@ -574,10 +728,20 @@ const user = useEntity(userStorage, 'user123');
574
728
  const users = useEntities(userStorage, userIds);
575
729
  const posts = useIndex(postStorage, categoryIndex, 'tech');
576
730
 
577
- // v1.x - Reactive collections
731
+ // v1.x - Reactive collections with typed indexes
578
732
  const user = useSelectEntityByPk(reactiveCollection, 'user123');
579
733
  const users = useSelectEntitiesByPks(reactiveCollection, userIds);
580
- const posts = useSelectEntitiesByIndexKey(reactiveCollection, reactiveIndex, 'tech');
734
+ // Use appropriate hook based on index type
735
+ const posts = useSelectEntitiesByIndexKeySetBased(
736
+ reactiveCollection,
737
+ reactiveIndexSetBased,
738
+ 'tech'
739
+ );
740
+ const orderedItems = useSelectEntitiesByIndexKeyArrayBased(
741
+ reactiveCollection,
742
+ reactiveIndexArrayBased,
743
+ 'category1'
744
+ );
581
745
  ```
582
746
 
583
747
  ### Collection Creation