@oimdb/react 1.3.0 → 2.0.1

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
@@ -1,20 +1,18 @@
1
1
  # @oimdb/react
2
2
 
3
- React integration for OIMDB - Hooks for selection and subscription with reactive collections and indexes.
3
+ React hooks and context helpers for OIMDB reactive collections, objects, and indexes.
4
4
 
5
- ## Overview
5
+ 📖 **[Full documentation](https://abaikov.github.io/oimdb/)**
6
6
 
7
- `@oimdb/react` provides React hooks that work with OIMDB reactive objects (`OIMReactiveCollection` and `OIMReactiveIndex`). The library includes both direct hooks for component-level usage and React Context utilities for application-wide data management.
7
+ ## For AI Assistants
8
8
 
9
- ## Features
9
+ If you are an AI assistant, read the full API reference before writing any OIMDB code:
10
10
 
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[]`)
13
- - **Automatic Subscription**: Uses `useSyncExternalStore` for optimal React 18+ performance
14
- - **Event Coalescing**: Leverages OIMDB's built-in event coalescing for efficient updates
15
- - **Type Safety**: Full TypeScript support with advanced generic type inference
16
- - **Context Support**: Optional React Context for centralized collection management
17
- - **Flexible Usage**: Use hooks directly or through context provider pattern
11
+ ```
12
+ node_modules/@oimdb/core/llms.txt
13
+ ```
14
+
15
+ CDN fallback: `https://cdn.jsdelivr.net/npm/@oimdb/core/llms.txt`
18
16
 
19
17
  ## Installation
20
18
 
@@ -22,769 +20,131 @@ React integration for OIMDB - Hooks for selection and subscription with reactive
22
20
  npm install @oimdb/react @oimdb/core
23
21
  ```
24
22
 
25
- ## Usage
23
+ ## Model
26
24
 
27
- ### Basic Setup
25
+ Collections own entities. Indexes, command streams, and other derived state live next to collections in your application schema.
28
26
 
29
27
  ```typescript
30
- import {
31
- OIMEventQueue,
32
- OIMRICollection,
33
- OIMReactiveIndexManualSetBased,
34
- OIMReactiveIndexManualArrayBased
28
+ import {
29
+ OIMEventQueue,
30
+ OIMReactiveCollection,
31
+ OIMReactiveCollectionIndexManualSetBased,
35
32
  } from '@oimdb/core';
36
- import {
37
- useSelectEntitiesByPks,
38
- useSelectEntitiesByIndexKeySetBased,
39
- useSelectEntitiesByIndexKeyArrayBased,
40
- useSelectEntityByPk
41
- } from '@oimdb/react';
42
-
43
- // Create event queue and reactive collections
44
- const queue = new OIMEventQueue();
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
-
52
- const usersCollection = new OIMRICollection(queue, {
53
- collectionOpts: { selectPk: (user: User) => user.id },
54
- indexes: { byTeam: userTeamIndex },
55
- });
56
- ```
57
-
58
- ### Single Entity Selection
59
-
60
- ```typescript
61
- function UserProfile({ userId }: { userId: string }) {
62
- const user = useSelectEntityByPk(usersCollection, userId);
63
-
64
- if (!user) return <div>Loading...</div>;
65
-
66
- return (
67
- <div>
68
- <h2>{user.name}</h2>
69
- <p>Email: {user.email}</p>
70
- </div>
71
- );
72
- }
73
- ```
74
-
75
- ### Multiple Entities Selection
76
-
77
- ```typescript
78
- function UserList({ userIds }: { userIds: string[] }) {
79
- const users = useSelectEntitiesByPks(usersCollection, userIds);
80
-
81
- return (
82
- <ul>
83
- {users.map((user, index) => (
84
- <li key={user?.id || index}>
85
- {user ? user.name : 'Loading...'}
86
- </li>
87
- ))}
88
- </ul>
89
- );
90
- }
91
- ```
92
-
93
- ### Index-based Selection
94
-
95
- OIMDB provides separate hooks for SetBased and ArrayBased indexes:
96
33
 
97
- #### SetBased Indexes (returns Set)
98
-
99
- ```typescript
100
- import { useSelectEntitiesByIndexKeySetBased, useSelectPksByIndexKeySetBased } from '@oimdb/react';
101
-
102
- function TeamMembers({ teamId }: { teamId: string }) {
103
- // For SetBased indexes, use SetBased hooks
104
- const teamUsers = useSelectEntitiesByIndexKeySetBased(
105
- usersCollection,
106
- usersCollection.indexes.byTeam, // OIMReactiveIndexManualSetBased
107
- teamId
108
- );
109
-
110
- // Or get just the PKs as Set
111
- const teamUserIds = useSelectPksByIndexKeySetBased(
112
- usersCollection.indexes.byTeam,
113
- teamId
114
- ); // Returns Set<string>
115
-
116
- return (
117
- <div>
118
- {teamUsers.map((user, index) => (
119
- <div key={user?.id || index}>
120
- {user ? `${user.name} (${user.role})` : 'Loading...'}
121
- </div>
122
- ))}
123
- </div>
124
- );
125
- }
126
- ```
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
-
159
- ## React Context Integration
160
-
161
- For applications with multiple collections, use the React Context pattern for centralized management:
162
-
163
- ### Context Setup
164
-
165
- ```typescript
166
- import {
167
- OIMRICollectionsProvider,
168
- useOIMCollectionsContext
169
- } from '@oimdb/react';
170
-
171
- interface User {
34
+ type User = {
172
35
  id: string;
173
- name: string;
174
36
  teamId: string;
175
- }
176
-
177
- interface Team {
178
- id: string;
179
- name: string;
180
- }
181
-
182
- function createCollections() {
183
- const queue = new OIMEventQueue();
184
-
185
- // Use SetBased for frequent add/remove operations
186
- const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
187
- const usersCollection = new OIMRICollection(queue, {
188
- collectionOpts: { selectPk: (user: User) => user.id },
189
- indexes: { byTeam: userTeamIndex },
190
- });
191
-
192
- const teamsCollection = new OIMRICollection(queue, {
193
- collectionOpts: { selectPk: (team: Team) => team.id },
194
- indexes: {},
195
- });
196
-
197
- return { users: usersCollection, teams: teamsCollection } as const;
198
- }
199
-
200
- type AppCollections = ReturnType<typeof createCollections>;
201
- ```
202
-
203
- ## TypeScript Typing Strategies
204
-
205
- For maximum type safety, you should properly type your collections dictionary. There are two main approaches, similar to how Redux handles state typing:
206
-
207
- ### Approach 1: Using `typeof` (Recommended for Simple Cases)
208
-
209
- The simplest approach is to use TypeScript's `typeof` operator to infer types from your collection instances:
210
-
211
- ```typescript
212
- import { OIMEventQueue, OIMRICollection, OIMReactiveIndexManual } from '@oimdb/core';
213
-
214
- interface User {
215
- id: string;
216
37
  name: string;
217
- teamId: string;
218
- }
219
-
220
- interface Team {
221
- id: string;
222
- name: string;
223
- }
38
+ };
224
39
 
225
- // Create collections
226
40
  const queue = new OIMEventQueue();
227
- const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
228
- const usersCollection = new OIMRICollection(queue, {
229
- collectionOpts: { selectPk: (user: User) => user.id },
230
- indexes: { byTeam: userTeamIndex },
231
- });
232
41
 
233
- const teamsCollection = new OIMRICollection(queue, {
234
- collectionOpts: { selectPk: (team: Team) => team.id },
235
- indexes: {},
42
+ const users = new OIMReactiveCollection<User, string>(queue, {
43
+ selectPk: (user) => user.id,
236
44
  });
237
45
 
238
- // Infer types using typeof
239
- const collections = {
240
- users: usersCollection,
241
- teams: teamsCollection,
242
- } as const;
243
-
244
- // Extract the type
245
- type AppCollections = typeof collections;
246
- ```
247
-
248
- **Usage:**
249
- ```typescript
250
- function MyComponent() {
251
- const { users, teams } = useOIMCollectionsContext<AppCollections>();
252
- // users and teams are fully typed with all their generics preserved
253
- }
46
+ const indexes = {
47
+ usersByTeam: new OIMReactiveCollectionIndexManualSetBased<
48
+ string,
49
+ string,
50
+ User
51
+ >(queue, { collection: users }),
52
+ };
254
53
  ```
255
54
 
256
- ### Approach 2: Creating Explicit Types (Recommended for Complex Projects)
257
-
258
- For larger applications or when you need more control, create explicit type definitions similar to Redux's approach:
55
+ The React context stores collections only:
259
56
 
260
57
  ```typescript
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';
271
-
272
- interface User {
273
- id: string;
274
- name: string;
275
- teamId: string;
276
- }
277
-
278
- interface Team {
279
- id: string;
280
- name: string;
281
- }
282
-
283
- // Define your collection types explicitly
284
- type UserCollection = OIMRICollection<
285
- User,
286
- string,
287
- 'byTeam',
288
- string,
289
- OIMIndexSetBased<string, string>,
290
- OIMReactiveIndexSetBased<string, string, OIMIndexSetBased<string, string>>
291
- >;
292
-
293
- type TeamCollection = OIMRICollection<
294
- Team,
295
- string,
296
- never,
297
- never,
298
- OIMIndexSetBased<never, never>,
299
- OIMReactiveIndexSetBased<never, never, OIMIndexSetBased<never, never>>
300
- >;
301
-
302
- // Define your collections dictionary type
303
- interface AppCollections {
304
- users: UserCollection;
305
- teams: TeamCollection;
306
- }
58
+ import { OIMCollectionsProvider } from '@oimdb/react';
307
59
 
308
- // Factory function that returns properly typed collections
309
- function createCollections(): AppCollections {
310
- const queue = new OIMEventQueue();
311
- const userTeamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
312
-
313
- return {
314
- users: new OIMRICollection(queue, {
315
- collectionOpts: { selectPk: (user: User) => user.id },
316
- indexes: { byTeam: userTeamIndex },
317
- }) as UserCollection,
318
-
319
- teams: new OIMRICollection(queue, {
320
- collectionOpts: { selectPk: (team: Team) => team.id },
321
- indexes: {},
322
- }) as TeamCollection,
323
- };
324
- }
325
- ```
60
+ const collections = { users };
326
61
 
327
- **Usage:**
328
- ```typescript
329
- function MyComponent() {
330
- const { users, teams } = useOIMCollectionsContext<AppCollections>();
331
- // Full type safety with explicit types
332
- }
333
- ```
334
-
335
- ### When to Use Each Approach
336
-
337
- - **Use `typeof`** when:
338
- - You have simple collection setups
339
- - You want TypeScript to infer everything automatically
340
- - You prefer less boilerplate
341
- - Your collections are created in one place
342
-
343
- - **Use explicit types** when:
344
- - You need to share types across multiple files
345
- - You want to document your data structure explicitly
346
- - You're building a library or shared module
347
- - You need to ensure type consistency across your application
348
- - You prefer Redux-style explicit typing patterns
349
-
350
- ### Provider Setup
351
-
352
- ```typescript
353
62
  function App() {
354
- const collections = React.useMemo(() => createCollections(), []);
355
-
356
63
  return (
357
- <OIMRICollectionsProvider collections={collections}>
358
- <UserDashboard />
359
- </OIMRICollectionsProvider>
64
+ <OIMCollectionsProvider collections={collections}>
65
+ <UsersList />
66
+ </OIMCollectionsProvider>
360
67
  );
361
68
  }
362
69
  ```
363
70
 
364
- ### Using Context in Components
71
+ Keep indexes in your module, app schema, props, or a separate context:
365
72
 
366
73
  ```typescript
367
- function UserDashboard() {
368
- const { users, teams } = useOIMCollectionsContext<AppCollections>();
369
-
370
- // Use collections with hooks
371
- const allUsers = useSelectEntitiesByPks(users, []);
372
- // Use appropriate hook based on index type
373
- const teamMembers = useSelectEntitiesByIndexKeySetBased(
374
- users,
375
- users.indexes.byTeam, // SetBased index
376
- 'team1'
377
- );
378
-
379
- return (
380
- <div>
381
- <h2>All Users: {allUsers.length}</h2>
382
- <h3>Team 1 Members: {teamMembers.length}</h3>
383
- </div>
384
- );
385
- }
386
- ```
74
+ import {
75
+ useOIMCollectionsContext,
76
+ useSelectEntitiesByIndexKeySetBased,
77
+ } from '@oimdb/react';
387
78
 
388
- ### Custom Context
79
+ type AppCollections = typeof collections;
389
80
 
390
- For multiple independent contexts:
81
+ function TeamUsers({ teamId }: { teamId: string }) {
82
+ const { users } = useOIMCollectionsContext<AppCollections>();
391
83
 
392
- ```typescript
393
- const UserContext = createOIMCollectionsContext<{ users: typeof usersCollection }>();
84
+ const teamUsers = useSelectEntitiesByIndexKeySetBased(
85
+ users,
86
+ indexes.usersByTeam,
87
+ teamId
88
+ );
394
89
 
395
- function UserProvider({ children }: { children: React.ReactNode }) {
396
- const collections = React.useMemo(() => ({ users: usersCollection }), []);
397
-
398
90
  return (
399
- <OIMRICollectionsProvider collections={collections} context={UserContext}>
400
- {children}
401
- </OIMRICollectionsProvider>
91
+ <ul>
92
+ {teamUsers?.map((user) =>
93
+ user ? <li key={user.id}>{user.name}</li> : null
94
+ )}
95
+ </ul>
402
96
  );
403
97
  }
404
-
405
- function UserComponent() {
406
- const { users } = useOIMCollectionsContext(UserContext);
407
- // Use users collection...
408
- }
409
- ```
410
-
411
- ## API Reference
412
-
413
- ### `useSelectEntityByPk(reactiveCollection, pk)`
414
-
415
- Subscribes to a single entity from a reactive collection.
416
-
417
- **Parameters:**
418
- - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
419
- - `pk: TPk` - Primary key of the entity
420
-
421
- **Returns:**
422
- - `TEntity | undefined` - Entity data or undefined if not found
423
-
424
- ### `useSelectEntitiesByPks(reactiveCollection, pks)`
425
-
426
- Subscribes to multiple entities from a reactive collection.
427
-
428
- **Parameters:**
429
- - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
430
- - `pks: readonly TPk[]` - Array of primary keys
431
-
432
- **Returns:**
433
- - `(TEntity | undefined)[]` - Array of entities (undefined for missing entities)
434
-
435
- ### Index-based Selection Hooks
436
-
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.
444
-
445
- **Parameters:**
446
- - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
447
- - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
448
- - `key: TKey` - Index key to query
449
-
450
- **Returns:**
451
- - `(TEntity | undefined)[]` - Array of entities for the given index key
452
-
453
- ##### `useSelectEntitiesByIndexKeysSetBased(reactiveCollection, reactiveIndex, keys)`
454
-
455
- Subscribes to entities indexed by multiple keys from a SetBased index.
456
-
457
- **Parameters:**
458
- - `reactiveCollection: OIMReactiveCollection<TEntity, TPk>` - Reactive collection instance
459
- - `reactiveIndex: OIMReactiveIndexSetBased<TKey, TPk, TIndex>` - SetBased reactive index instance
460
- - `keys: readonly TKey[]` - Array of index keys to query
461
-
462
- **Returns:**
463
- - `(TEntity | undefined)[]` - Array of entities for the given index keys
464
-
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.
504
-
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.
516
-
517
- **Parameters:**
518
- - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
519
- - `key: TKey` - Index key to query
520
-
521
- **Returns:**
522
- - `TPk[]` - Array of primary keys for the given index key (preserves order/sorting)
523
-
524
- ##### `useSelectPksByIndexKeysArrayBased(reactiveIndex, keys)`
525
-
526
- Subscribes to primary keys indexed by multiple keys from an ArrayBased index.
527
-
528
- **Parameters:**
529
- - `reactiveIndex: OIMReactiveIndexArrayBased<TKey, TPk, TIndex>` - ArrayBased reactive index instance
530
- - `keys: readonly TKey[]` - Array of index keys to query
531
-
532
- **Returns:**
533
- - `Map<TKey, TPk[]>` - Map of index keys to their corresponding primary key arrays (preserves order)
534
-
535
- ## Context API Reference
536
-
537
- ### `OIMRICollectionsProvider<T>`
538
-
539
- Provider component for collections context.
540
-
541
- **Props:**
542
- - `collections: T` - Dictionary of reactive collections
543
- - `children: ReactNode` - React children
544
- - `context?: React.Context<OIMContextValue<T>>` - Optional custom context
545
-
546
- ### `useOIMCollectionsContext<T>(context?)`
547
-
548
- Hook to access collections from context.
549
-
550
- **Parameters:**
551
- - `context?: React.Context<OIMContextValue<T>>` - Optional custom context
552
-
553
- **Returns:**
554
- - `T` - Collections dictionary with full type safety
555
-
556
- **Throws:**
557
- - Error if used outside of provider
558
-
559
- ### `createOIMCollectionsContext<T>()`
560
-
561
- Creates a custom collections context with specific typing.
562
-
563
- **Returns:**
564
- - `React.Context<OIMContextValue<T>>` - Typed React context
565
-
566
- ### Type Utilities
567
-
568
- #### `CollectionsDictionary`
569
-
570
- Base type for any collections dictionary. Use `typeof` to extract types from your collection instances, or define explicit types using `OIMRICollection` generics.
571
-
572
- ## Architecture
573
-
574
- ### Reactive Collections Integration
575
-
576
- The hooks work directly with OIMDB reactive objects:
577
-
578
- ```typescript
579
- // Use reactive collections and indexes directly
580
- const user = useSelectEntityByPk(reactiveCollection, 'user123');
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
- );
594
98
  ```
595
99
 
596
- ### Event Subscription
597
-
598
- Hooks automatically subscribe to OIMDB reactive events using `useSyncExternalStore`:
100
+ ## Hooks
599
101
 
600
- - **Collection updates**: Subscribe to `reactiveCollection.updateEventEmitter`
601
- - **Index updates**: Subscribe to `reactiveIndex.updateEventEmitter`
602
- - **Optimized subscriptions**: Subscribe only to specific keys for efficient updates
603
- - **Automatic cleanup**: Unsubscribe when component unmounts
102
+ ### Collections
604
103
 
605
- ### Index Type Selection
104
+ - `useSelectEntityByPk(collection, pk)` watches one entity.
105
+ - `useSelectEntitiesByPks(collection, pks)` watches multiple entities.
606
106
 
607
- When working with indexes, choose the appropriate hook based on your index type:
107
+ ### Set-Based Indexes
608
108
 
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)
109
+ - `useSelectPksByIndexKeySetBased(index, key)` watches PKs for one index key.
110
+ - `useSelectPksByIndexKeysSetBased(index, keys)` watches PKs for several index keys.
111
+ - `useSelectEntitiesByIndexKeySetBased(collection, index, key)` watches entities referenced by one index key.
112
+ - `useSelectEntitiesByIndexKeysSetBased(collection, index, keys)` watches entities referenced by several index keys.
611
113
 
612
- This ensures type safety and correct return types. TypeScript will enforce the correct hook usage based on your index type.
114
+ ### Array-Based Indexes
613
115
 
614
- ### Performance
116
+ - `useSelectPksByIndexKeyArrayBased(index, key)` watches PKs for one index key.
117
+ - `useSelectPksByIndexKeysArrayBased(index, keys)` watches PKs for several index keys.
118
+ - `useSelectEntitiesByIndexKeyArrayBased(collection, index, key)` watches entities referenced by one index key.
119
+ - `useSelectEntitiesByIndexKeysArrayBased(collection, index, keys)` watches entities referenced by several index keys.
615
120
 
616
- - **React 18+ Integration**: Uses `useSyncExternalStore` for optimal performance
617
- - **Event Coalescing**: OIMDB's built-in event coalescing reduces unnecessary re-renders
618
- - **Key-specific subscriptions**: Only listen to changes for relevant data
619
- - **Memory Management**: Automatic cleanup prevents memory leaks
620
- - **Efficient batching**: Updates are batched through React's concurrent features
121
+ ### Reactive Objects
621
122
 
622
- ## Examples
123
+ - `useSelectValueByObjectKey(object, key)` watches one object key.
124
+ - `useSelectValuesByObjectKeys(object, keys)` watches several object keys.
623
125
 
624
- ### Complete Example
126
+ ## Context API
625
127
 
626
128
  ```typescript
627
- import React from '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
129
+ import {
130
+ OIMCollectionsProvider,
131
+ createOIMCollectionsContext,
132
+ useOIMCollectionsContext,
639
133
  } from '@oimdb/react';
640
-
641
- interface User {
642
- id: string;
643
- name: string;
644
- email: string;
645
- teamId: string;
646
- }
647
-
648
- // Setup
649
- function createUserCollection() {
650
- const queue = new OIMEventQueue();
651
- // Use SetBased for frequent add/remove operations
652
- const teamIndex = new OIMReactiveIndexManualSetBased<string, string>(queue);
653
-
654
- return new OIMRICollection(queue, {
655
- collectionOpts: { selectPk: (user: User) => user.id },
656
- indexes: { byTeam: teamIndex },
657
- });
658
- }
659
-
660
- const usersCollection = createUserCollection();
661
-
662
- // Component
663
- function UserProfile({ userId }: { userId: string }) {
664
- const user = useSelectEntityByPk(usersCollection, userId);
665
-
666
- if (!user) return <div>Loading...</div>;
667
-
668
- return <h2>{user.name}</h2>;
669
- }
670
-
671
- function TeamDashboard({ teamId }: { teamId: string }) {
672
- // Use SetBased hook for SetBased index
673
- const teamMembers = useSelectEntitiesByIndexKeySetBased(
674
- usersCollection,
675
- usersCollection.indexes.byTeam, // OIMReactiveIndexManualSetBased
676
- teamId
677
- );
678
-
679
- return (
680
- <div>
681
- <h3>Team Members ({teamMembers.length})</h3>
682
- {teamMembers.map(user => (
683
- <div key={user?.id}>{user?.name}</div>
684
- ))}
685
- </div>
686
- );
687
- }
688
134
  ```
689
135
 
690
- ### With Context Provider
136
+ `OIMCollectionsProvider` accepts a dictionary of `OIMReactiveCollection` instances. Use `typeof collections` to preserve exact collection types:
691
137
 
692
138
  ```typescript
693
- import {
694
- OIMRICollectionsProvider,
695
- useOIMCollectionsContext
696
- } from '@oimdb/react';
139
+ const collections = {
140
+ users,
141
+ };
697
142
 
698
- function App() {
699
- const collections = React.useMemo(() => ({
700
- users: createUserCollection(),
701
- // ... other collections
702
- }), []);
703
-
704
- return (
705
- <OIMRICollectionsProvider collections={collections}>
706
- <Dashboard />
707
- </OIMRICollectionsProvider>
708
- );
709
- }
143
+ type AppCollections = typeof collections;
710
144
 
711
- function Dashboard() {
712
- const { users } = useOIMCollectionsContext();
713
- const allUsers = useSelectEntitiesByPks(users, []);
714
-
715
- return <div>Total Users: {allUsers.length}</div>;
145
+ function UserName({ userId }: { userId: string }) {
146
+ const { users } = useOIMCollectionsContext<AppCollections>();
147
+ const user = useSelectEntityByPk(users, userId);
148
+ return <span>{user?.name}</span>;
716
149
  }
717
150
  ```
718
-
719
- ## Migration from v0.x
720
-
721
- The v1.x API has changed significantly to work with reactive collections:
722
-
723
- ### Hook Name Changes
724
-
725
- ```typescript
726
- // v0.x - Abstract storage interfaces
727
- const user = useEntity(userStorage, 'user123');
728
- const users = useEntities(userStorage, userIds);
729
- const posts = useIndex(postStorage, categoryIndex, 'tech');
730
-
731
- // v1.x - Reactive collections with typed indexes
732
- const user = useSelectEntityByPk(reactiveCollection, 'user123');
733
- const users = useSelectEntitiesByPks(reactiveCollection, userIds);
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
- );
745
- ```
746
-
747
- ### Collection Creation
748
-
749
- ```typescript
750
- // v0.x - With DX layer
751
- const db = createDb({ scheduler: 'microtask' });
752
- const users = db.createCollection<User>();
753
- const user = useEntity(users.advanced.collection, userId);
754
-
755
- // v1.x - Direct reactive collections
756
- const queue = new OIMEventQueue();
757
- const usersCollection = new OIMRICollection(queue, {
758
- collectionOpts: { selectPk: (user: User) => user.id },
759
- indexes: {},
760
- });
761
- const user = useSelectEntityByPk(usersCollection, userId);
762
- ```
763
-
764
- ### Context API
765
-
766
- ```typescript
767
- // v0.x - No context support
768
-
769
- // v1.x - Full context support
770
- const collections = { users: usersCollection };
771
- <OIMRICollectionsProvider collections={collections}>
772
- <App />
773
- </OIMRICollectionsProvider>
774
- ```
775
-
776
- ### Key Changes
777
-
778
- - **Hook naming**: More explicit names like `useSelectEntityByPk` vs `useEntity`
779
- - **Parameters**: Direct reactive collection objects instead of storage abstractions
780
- - **Context**: New context API for centralized collection management
781
- - **Type safety**: Enhanced TypeScript support with better inference
782
-
783
- ## Dependencies
784
-
785
- - `@oimdb/core` - Core OIMDB functionality
786
- - `react` - React hooks and components
787
-
788
- ## License
789
-
790
- MIT