react-state-custom 1.0.10 → 1.0.12

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.
@@ -0,0 +1,883 @@
1
+ # React State Custom - API Documentation
2
+
3
+ A powerful React library for managing shared state and context with TypeScript support, built with Vite.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Core Context System](#core-context-system)
8
+ - [Context Class](#context-class)
9
+ - [getContext](#getcontext)
10
+ - [useDataContext](#usedatacontext)
11
+ 2. [Data Source Hooks](#data-source-hooks)
12
+ - [useDataSource](#usedatasource)
13
+ - [useDataSourceMultiple](#usedatasourcemultiple)
14
+ 3. [Data Subscription Hooks](#data-subscription-hooks)
15
+ - [useDataSubscribe](#usedatasubscribe)
16
+ - [useDataSubscribeMultiple](#usedatasubscribemultiple)
17
+ - [useDataSubscribeMultipleWithDebounce](#usedatasubscribemultiplewithbounce)
18
+ - [useDataSubscribeWithTransform](#usedatasubscribewithtransform)
19
+ 4. [Root Context Factory](#root-context-factory)
20
+ - [createRootCtx](#createrootctx)
21
+ 5. [Auto Context System](#auto-context-system)
22
+ - [AutoRootCtx](#autorootctx)
23
+ - [createAutoCtx](#createautoctx)
24
+ 6. [Utility Hooks](#utility-hooks)
25
+ - [useArrayHash](#usearrayhash)
26
+ - [useQuickSubscribe](#usequicksubscribe)
27
+ 7. [Usage Patterns](#usage-patterns)
28
+ 8. [Examples](#examples)
29
+
30
+ ---
31
+
32
+ ## Core Context System
33
+
34
+ ### Context Class
35
+
36
+ A generic context class for managing shared state and event subscriptions.
37
+
38
+ **Type Definition:**
39
+ ```typescript
40
+ class Context<D> {
41
+ constructor(name: string)
42
+ data: Partial<D>
43
+ registry: Set<string>
44
+ publish(key: keyof D, value: D[typeof key] | undefined): void
45
+ subscribe(key: keyof D, listener: (e: D[typeof key] | undefined) => void): () => void
46
+ }
47
+ ```
48
+
49
+ **Parameters:**
50
+ - `D` - The shape of the data managed by the context
51
+
52
+ **Properties:**
53
+ - `name` - The name of the context (for debugging)
54
+ - `data` - The current data held by the context
55
+ - `registry` - Registry for tracking active keys
56
+
57
+ **Methods:**
58
+ - `publish(key, value)` - Publish a value to the context and notify subscribers if it changed
59
+ - `subscribe(key, listener)` - Subscribe to changes for a specific key, returns unsubscribe function
60
+
61
+ **Example:**
62
+ ```typescript
63
+ interface UserData {
64
+ name: string;
65
+ age: number;
66
+ email: string;
67
+ }
68
+
69
+ const userContext = new Context<UserData>('user-context');
70
+
71
+ // Subscribe to changes
72
+ const unsubscribe = userContext.subscribe('name', (newName) => {
73
+ console.log('Name changed to:', newName);
74
+ });
75
+
76
+ // Publish data
77
+ userContext.publish('name', 'John Doe');
78
+ userContext.publish('age', 30);
79
+
80
+ // Cleanup
81
+ unsubscribe();
82
+ ```
83
+
84
+ ---
85
+
86
+ ### getContext
87
+
88
+ Get or create a memoized Context instance by name.
89
+
90
+ **Type Definition:**
91
+ ```typescript
92
+ function getContext(name: string): Context<any>
93
+ ```
94
+
95
+ **Parameters:**
96
+ - `name` - The context name
97
+
98
+ **Returns:**
99
+ - The Context instance (memoized by name)
100
+
101
+ **Example:**
102
+ ```typescript
103
+ const userCtx = getContext('user-context');
104
+ const settingsCtx = getContext('settings-context');
105
+
106
+ // Same name returns the same instance
107
+ const userCtx2 = getContext('user-context');
108
+ console.log(userCtx === userCtx2); // true
109
+ ```
110
+
111
+ ---
112
+
113
+ ### useDataContext
114
+
115
+ React hook to get a typed Context instance by name.
116
+
117
+ **Type Definition:**
118
+ ```typescript
119
+ function useDataContext<D>(name?: string): Context<D>
120
+ ```
121
+
122
+ **Parameters:**
123
+ - `name` - The context name (default: "noname")
124
+
125
+ **Returns:**
126
+ - The typed Context instance
127
+
128
+ **Example:**
129
+ ```typescript
130
+ interface AppState {
131
+ user: User;
132
+ theme: 'light' | 'dark';
133
+ isLoading: boolean;
134
+ }
135
+
136
+ function MyComponent() {
137
+ const ctx = useDataContext<AppState>('app-state');
138
+
139
+ // Now ctx is typed as Context<AppState>
140
+ return <div>Context ready</div>;
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Data Source Hooks
147
+
148
+ ### useDataSource
149
+
150
+ React hook to publish a value to the context when it changes.
151
+
152
+ **Type Definition:**
153
+ ```typescript
154
+ function useDataSource<D, K extends keyof D>(
155
+ ctx: Context<D> | undefined,
156
+ key: K,
157
+ value: D[K] | undefined
158
+ ): void
159
+ ```
160
+
161
+ **Parameters:**
162
+ - `ctx` - The context instance
163
+ - `key` - The key to update
164
+ - `value` - The new value
165
+
166
+ **Example:**
167
+ ```typescript
168
+ interface UserState {
169
+ profile: UserProfile;
170
+ preferences: UserPreferences;
171
+ }
172
+
173
+ function UserProvider({ userId }: { userId: string }) {
174
+ const ctx = useDataContext<UserState>('user-state');
175
+ const userProfile = useFetchUserProfile(userId);
176
+ const userPreferences = useFetchUserPreferences(userId);
177
+
178
+ // Automatically publish to context when values change
179
+ useDataSource(ctx, 'profile', userProfile);
180
+ useDataSource(ctx, 'preferences', userPreferences);
181
+
182
+ return <>{children}</>;
183
+ }
184
+ ```
185
+
186
+ ---
187
+
188
+ ### useDataSourceMultiple
189
+
190
+ React hook to publish multiple values to the context.
191
+
192
+ **Type Definition:**
193
+ ```typescript
194
+ function useDataSourceMultiple<D, T extends readonly (keyof D)[]>(
195
+ ctx: Context<D> | undefined,
196
+ ...entries: { -readonly [P in keyof T]: [T[P], D[T[P]]] }
197
+ ): void
198
+ ```
199
+
200
+ **Parameters:**
201
+ - `ctx` - The context instance
202
+ - `entries` - Array of [key, value] pairs to update
203
+
204
+ **Example:**
205
+ ```typescript
206
+ function AppProvider() {
207
+ const ctx = useDataContext<AppState>('app-state');
208
+ const user = useCurrentUser();
209
+ const theme = useTheme();
210
+ const isLoading = useLoadingState();
211
+
212
+ // Publish multiple values at once
213
+ useDataSourceMultiple(ctx,
214
+ ['user', user],
215
+ ['theme', theme],
216
+ ['isLoading', isLoading]
217
+ );
218
+
219
+ return <>{children}</>;
220
+ }
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Data Subscription Hooks
226
+
227
+ ### useDataSubscribe
228
+
229
+ React hook to subscribe to a context value, with optional debounce.
230
+
231
+ **Type Definition:**
232
+ ```typescript
233
+ function useDataSubscribe<D, K extends keyof D>(
234
+ ctx: Context<D> | undefined,
235
+ key: K,
236
+ debounceTime?: number
237
+ ): D[K] | undefined
238
+ ```
239
+
240
+ **Parameters:**
241
+ - `ctx` - The context instance
242
+ - `key` - The key to subscribe to
243
+ - `debounceTime` - Debounce time in ms (default: 0)
244
+
245
+ **Returns:**
246
+ - The current value for the key
247
+
248
+ **Example:**
249
+ ```typescript
250
+ function UserProfile() {
251
+ const ctx = useDataContext<UserState>('user-state');
252
+
253
+ // Subscribe to user profile changes
254
+ const profile = useDataSubscribe(ctx, 'profile');
255
+
256
+ // Subscribe with debouncing for frequently changing data
257
+ const searchQuery = useDataSubscribe(ctx, 'searchQuery', 300);
258
+
259
+ if (!profile) return <div>Loading...</div>;
260
+
261
+ return (
262
+ <div>
263
+ <h1>{profile.name}</h1>
264
+ <p>{profile.email}</p>
265
+ </div>
266
+ );
267
+ }
268
+ ```
269
+
270
+ ---
271
+
272
+ ### useDataSubscribeMultiple
273
+
274
+ React hook to subscribe to multiple context values.
275
+
276
+ **Type Definition:**
277
+ ```typescript
278
+ function useDataSubscribeMultiple<D, K extends keyof D>(
279
+ ctx: Context<D> | undefined,
280
+ ...keys: K[]
281
+ ): Pick<D, K>
282
+ ```
283
+
284
+ **Parameters:**
285
+ - `ctx` - The context instance
286
+ - `keys` - Keys to subscribe to
287
+
288
+ **Returns:**
289
+ - An object with the current values for the keys
290
+
291
+ **Example:**
292
+ ```typescript
293
+ function Dashboard() {
294
+ const ctx = useDataContext<AppState>('app-state');
295
+
296
+ // Subscribe to multiple values
297
+ const { user, theme, isLoading } = useDataSubscribeMultiple(
298
+ ctx,
299
+ 'user',
300
+ 'theme',
301
+ 'isLoading'
302
+ );
303
+
304
+ return (
305
+ <div className={`dashboard ${theme}`}>
306
+ {isLoading && <Spinner />}
307
+ <h1>Welcome, {user?.name}</h1>
308
+ </div>
309
+ );
310
+ }
311
+ ```
312
+
313
+ ---
314
+
315
+ ### useDataSubscribeMultipleWithDebounce
316
+
317
+ React hook to subscribe to multiple context values with throttling.
318
+
319
+ **Type Definition:**
320
+ ```typescript
321
+ function useDataSubscribeMultipleWithDebounce<D, K extends (keyof D)[]>(
322
+ ctx: Context<D> | undefined,
323
+ debounceTime?: number,
324
+ ...keys: K
325
+ ): { [i in keyof K]: D[K[i]] | undefined }
326
+ ```
327
+
328
+ **Parameters:**
329
+ - `ctx` - The context instance
330
+ - `debounceTime` - Debounce time in ms (default: 50)
331
+ - `keys` - Keys to subscribe to
332
+
333
+ **Returns:**
334
+ - Array of current values for the keys
335
+
336
+ **Example:**
337
+ ```typescript
338
+ function SearchResults() {
339
+ const ctx = useDataContext<SearchState>('search-state');
340
+
341
+ // Subscribe with debouncing for performance
342
+ const [query, filters, sortBy] = useDataSubscribeMultipleWithDebounce(
343
+ ctx,
344
+ 200, // 200ms debounce
345
+ 'query',
346
+ 'filters',
347
+ 'sortBy'
348
+ );
349
+
350
+ const results = useSearchResults(query, filters, sortBy);
351
+
352
+ return <ResultsList results={results} />;
353
+ }
354
+ ```
355
+
356
+ ---
357
+
358
+ ### useDataSubscribeWithTransform
359
+
360
+ React hook to subscribe to a context value and transform it before returning.
361
+
362
+ **Type Definition:**
363
+ ```typescript
364
+ function useDataSubscribeWithTransform<D, K extends keyof D, E>(
365
+ ctx: Context<D> | undefined,
366
+ key: K,
367
+ transform: (e: D[K] | undefined) => E
368
+ ): E
369
+ ```
370
+
371
+ **Parameters:**
372
+ - `ctx` - The context instance
373
+ - `key` - The key to subscribe to
374
+ - `transform` - Function to transform the value
375
+
376
+ **Returns:**
377
+ - The transformed value
378
+
379
+ **Example:**
380
+ ```typescript
381
+ function UserStats() {
382
+ const ctx = useDataContext<UserState>('user-state');
383
+
384
+ // Transform user data to display stats
385
+ const userStats = useDataSubscribeWithTransform(
386
+ ctx,
387
+ 'profile',
388
+ (profile) => ({
389
+ totalPosts: profile?.posts?.length || 0,
390
+ joinedDate: profile?.createdAt ? new Date(profile.createdAt) : null,
391
+ isVerified: profile?.verified || false
392
+ })
393
+ );
394
+
395
+ return (
396
+ <div>
397
+ <p>Posts: {userStats.totalPosts}</p>
398
+ <p>Joined: {userStats.joinedDate?.toLocaleDateString()}</p>
399
+ {userStats.isVerified && <Badge>Verified</Badge>}
400
+ </div>
401
+ );
402
+ }
403
+ ```
404
+
405
+ ---
406
+
407
+ ## Root Context Factory
408
+
409
+ ### createRootCtx
410
+
411
+ Factory that creates a headless "Root" component and companion hooks for a context namespace.
412
+
413
+ **Type Definition:**
414
+ ```typescript
415
+ function createRootCtx<U extends object, V extends object>(
416
+ name: string,
417
+ useFn: (e: U) => V
418
+ ): {
419
+ Root: React.FC<U>;
420
+ useCtxState: (e: U) => Context<V>;
421
+ useCtxStateStrict: (e: U) => Context<V>;
422
+ resolveCtxName: (e: U) => string;
423
+ }
424
+ ```
425
+
426
+ **Parameters:**
427
+ - `name` - Base name for the context
428
+ - `useFn` - Hook function that computes state from props
429
+
430
+ **Returns:**
431
+ - `Root` - Component to mount for providing the context
432
+ - `useCtxState` - Lenient consumer hook
433
+ - `useCtxStateStrict` - Strict consumer hook (throws if Root not mounted)
434
+ - `resolveCtxName` - Function to resolve context name from props
435
+
436
+ **Example:**
437
+ ```typescript
438
+ // Define your state hook
439
+ function useUserState(props: { userId: string }) {
440
+ const [user, setUser] = useState(null);
441
+ const [loading, setLoading] = useState(true);
442
+
443
+ useEffect(() => {
444
+ fetchUser(props.userId).then(user => {
445
+ setUser(user);
446
+ setLoading(false);
447
+ });
448
+ }, [props.userId]);
449
+
450
+ return { user, loading };
451
+ }
452
+
453
+ // Create the root context
454
+ const { Root: UserRoot, useCtxState: useUserCtxState } = createRootCtx(
455
+ 'user-state',
456
+ useUserState
457
+ );
458
+
459
+ // Provider component
460
+ function UserProvider({ userId, children }: { userId: string; children: React.ReactNode }) {
461
+ return (
462
+ <>
463
+ <UserRoot userId={userId} />
464
+ {children}
465
+ </>
466
+ );
467
+ }
468
+
469
+ // Consumer component
470
+ function UserProfile({ userId }: { userId: string }) {
471
+ const ctx = useUserCtxState({ userId });
472
+ const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
473
+
474
+ if (loading) return <div>Loading...</div>;
475
+
476
+ return <div>Hello, {user?.name}</div>;
477
+ }
478
+ ```
479
+
480
+ ---
481
+
482
+ ## Auto Context System
483
+
484
+ ### AutoRootCtx
485
+
486
+ Component for automatic context management. Mount once at the app root to enable automatic Root instance management.
487
+
488
+ **Type Definition:**
489
+ ```typescript
490
+ function AutoRootCtx({ Wrapper }: {
491
+ Wrapper?: React.ComponentType<{ children: React.ReactNode }>
492
+ }): JSX.Element
493
+ ```
494
+
495
+ **Parameters:**
496
+ - `Wrapper` - Optional wrapper component (should act like ErrorBoundary)
497
+
498
+ **Example:**
499
+ ```typescript
500
+ // At your app root
501
+ function App() {
502
+ return (
503
+ <>
504
+ <AutoRootCtx Wrapper={ErrorBoundary}/>
505
+ <Router>
506
+ <Routes>
507
+ <Route path="/user/:id" element={<UserPage />} />
508
+ </Routes>
509
+ </Router>
510
+ </>
511
+ );
512
+ }
513
+
514
+ // ErrorBoundary wrapper
515
+ function ErrorBoundary({ children }: { children: React.ReactNode }) {
516
+ // Your error boundary logic
517
+ return <>{children}</>;
518
+ }
519
+ ```
520
+
521
+ ---
522
+
523
+ ### createAutoCtx
524
+
525
+ Bridges a Root context (from createRootCtx) to the global AutoRootCtx renderer.
526
+
527
+ **Type Definition:**
528
+ ```typescript
529
+ function createAutoCtx<U extends object, V extends object>(
530
+ rootContext: ReturnType<typeof createRootCtx<U, V>>
531
+ ): {
532
+ useCtxState: (e: U) => Context<V>;
533
+ }
534
+ ```
535
+
536
+ **Parameters:**
537
+ - `rootContext` - Return value from createRootCtx
538
+
539
+ **Returns:**
540
+ - `useCtxState` - Hook that automatically manages Root instances
541
+
542
+ **Example:**
543
+ ```typescript
544
+ // Create auto context from root context
545
+ const { useCtxState: useUserCtxStateAuto } = createAutoCtx(
546
+ createRootCtx('user-state', useUserState)
547
+ );
548
+
549
+ // Usage - no need to manually mount Root components
550
+ function UserProfile({ userId }: { userId: string }) {
551
+ // This automatically manages the Root instance
552
+ const ctx = useUserCtxStateAuto({ userId });
553
+ const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
554
+
555
+ if (loading) return <div>Loading...</div>;
556
+
557
+ return <div>Hello, {user?.name}</div>;
558
+ }
559
+ ```
560
+
561
+ ---
562
+
563
+ ## Utility Hooks
564
+
565
+ ### useArrayHash
566
+
567
+ A custom hook that computes a stable hash for an array of values.
568
+
569
+ **Type Definition:**
570
+ ```typescript
571
+ function useArrayHash(e: any[]): string
572
+ ```
573
+
574
+ **Parameters:**
575
+ - `e` - The input array to hash
576
+
577
+ **Returns:**
578
+ - A string hash that updates when the array changes
579
+
580
+ **Example:**
581
+ ```typescript
582
+ function OptimizedComponent({ items }: { items: any[] }) {
583
+ // Get stable hash for the array
584
+ const itemsHash = useArrayHash(items);
585
+
586
+ // Only recalculate when hash changes
587
+ const processedItems = useMemo(() => {
588
+ return items.map(item => expensiveProcessing(item));
589
+ }, [itemsHash]);
590
+
591
+ return <div>{processedItems.length} items processed</div>;
592
+ }
593
+ ```
594
+
595
+ ---
596
+
597
+ ### useQuickSubscribe
598
+
599
+ Hook for efficiently subscribing to specific properties of a context's data object.
600
+
601
+ **Type Definition:**
602
+ ```typescript
603
+ function useQuickSubscribe<D>(
604
+ ctx: Context<D> | undefined
605
+ ): { [P in keyof D]?: D[P] | undefined }
606
+ ```
607
+
608
+ **Parameters:**
609
+ - `ctx` - The context object containing data and a subscribe method
610
+
611
+ **Returns:**
612
+ - A proxy object that mirrors the context data, automatically subscribing to accessed properties
613
+
614
+ **Example:**
615
+ ```typescript
616
+ function UserComponent() {
617
+ const ctx = useDataContext<UserState>('user-state');
618
+
619
+ // Only subscribes to properties you actually access
620
+ const { name, email } = useQuickSubscribe(ctx);
621
+ // Accessing 'name' and 'email' will subscribe to changes in those properties only
622
+
623
+ return (
624
+ <div>
625
+ <h1>{name}</h1>
626
+ <p>{email}</p>
627
+ {/* If you later access 'age', it will automatically subscribe to that too */}
628
+ </div>
629
+ );
630
+ }
631
+ ```
632
+
633
+ ---
634
+
635
+ ## Usage Patterns
636
+
637
+ ### Basic Context Usage
638
+
639
+ ```typescript
640
+ // 1. Define your data interface
641
+ interface AppState {
642
+ user: User | null;
643
+ theme: 'light' | 'dark';
644
+ notifications: Notification[];
645
+ }
646
+
647
+ // 2. Create and use context
648
+ function App() {
649
+ const ctx = useDataContext<AppState>('app-state');
650
+
651
+ // 3. Provide data
652
+ const user = useCurrentUser();
653
+ const theme = useTheme();
654
+ useDataSource(ctx, 'user', user);
655
+ useDataSource(ctx, 'theme', theme);
656
+
657
+ return <AppContent />;
658
+ }
659
+
660
+ // 4. Consume data
661
+ function AppContent() {
662
+ const ctx = useDataContext<AppState>('app-state');
663
+ const { user, theme } = useDataSubscribeMultiple(ctx, 'user', 'theme');
664
+
665
+ return <div className={`app ${theme}`}>Welcome, {user?.name}</div>;
666
+ }
667
+ ```
668
+
669
+ ### Advanced Root Context Pattern
670
+
671
+ ```typescript
672
+ // 1. Create state hook
673
+ function useAppState(props: { initialTheme: string }) {
674
+ const [theme, setTheme] = useState(props.initialTheme);
675
+ const [user, setUser] = useState(null);
676
+
677
+ return { theme, user, setTheme, setUser };
678
+ }
679
+
680
+ // 2. Create root context
681
+ const { Root: AppRoot, useCtxState } = createRootCtx('app', useAppState);
682
+
683
+ // 3. Provider pattern
684
+ function AppProvider({ children }: { children: React.ReactNode }) {
685
+ return (
686
+ <>
687
+ <AppRoot initialTheme="light" />
688
+ {children}
689
+ </>
690
+ );
691
+ }
692
+
693
+ // 4. Consumer hook
694
+ function useAppContext() {
695
+ return useCtxState({ initialTheme: 'light' });
696
+ }
697
+ ```
698
+
699
+ ### Auto Context Pattern
700
+
701
+ ```typescript
702
+ // 1. Setup auto context once
703
+ const { useCtxState: useAppState } = createAutoCtx(
704
+ createRootCtx('app-auto', useAppStateLogic)
705
+ );
706
+
707
+ // 2. Mount AutoRootCtx at app root
708
+ function App() {
709
+ return (
710
+ <AutoRootCtx Wrapper={ErrorBoundary}>
711
+ <MyApp />
712
+ </AutoRootCtx>
713
+ );
714
+ }
715
+
716
+ // 3. Use anywhere without manual Root mounting
717
+ function AnyComponent() {
718
+ const ctx = useAppState({ config: 'production' });
719
+ const data = useQuickSubscribe(ctx);
720
+
721
+ return <div>{data.someProperty}</div>;
722
+ }
723
+ ```
724
+
725
+ ---
726
+
727
+ ## Examples
728
+
729
+ ### Complete Todo App Example
730
+
731
+ ```typescript
732
+ // Types
733
+ interface TodoState {
734
+ todos: Todo[];
735
+ filter: 'all' | 'active' | 'completed';
736
+ newTodoText: string;
737
+ }
738
+
739
+ interface Todo {
740
+ id: string;
741
+ text: string;
742
+ completed: boolean;
743
+ }
744
+
745
+ // State hook
746
+ function useTodoState() {
747
+ const [todos, setTodos] = useState<Todo[]>([]);
748
+ const [filter, setFilter] = useState<'all' | 'active' | 'completed'>('all');
749
+ const [newTodoText, setNewTodoText] = useState('');
750
+
751
+ const addTodo = useCallback((text: string) => {
752
+ const newTodo: Todo = {
753
+ id: Date.now().toString(),
754
+ text,
755
+ completed: false
756
+ };
757
+ setTodos(prev => [...prev, newTodo]);
758
+ setNewTodoText('');
759
+ }, []);
760
+
761
+ const toggleTodo = useCallback((id: string) => {
762
+ setTodos(prev => prev.map(todo =>
763
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
764
+ ));
765
+ }, []);
766
+
767
+ return {
768
+ todos,
769
+ filter,
770
+ newTodoText,
771
+ setFilter,
772
+ setNewTodoText,
773
+ addTodo,
774
+ toggleTodo
775
+ };
776
+ }
777
+
778
+ // Auto context setup
779
+ const { useCtxState: useTodoContext } = createAutoCtx(
780
+ createRootCtx('todo-app', useTodoState)
781
+ );
782
+
783
+ // App component
784
+ function TodoApp() {
785
+ return (
786
+ <AutoRootCtx>
787
+ <div className="todo-app">
788
+ <TodoInput />
789
+ <TodoList />
790
+ <TodoFilters />
791
+ </div>
792
+ </AutoRootCtx>
793
+ );
794
+ }
795
+
796
+ // Input component
797
+ function TodoInput() {
798
+ const ctx = useTodoContext();
799
+ const { newTodoText, addTodo, setNewTodoText } = useQuickSubscribe(ctx);
800
+
801
+ const handleSubmit = (e: React.FormEvent) => {
802
+ e.preventDefault();
803
+ if (newTodoText.trim()) {
804
+ addTodo(newTodoText.trim());
805
+ }
806
+ };
807
+
808
+ return (
809
+ <form onSubmit={handleSubmit}>
810
+ <input
811
+ value={newTodoText}
812
+ onChange={(e) => setNewTodoText(e.target.value)}
813
+ placeholder="Add a new todo..."
814
+ />
815
+ <button type="submit">Add</button>
816
+ </form>
817
+ );
818
+ }
819
+
820
+ // List component
821
+ function TodoList() {
822
+ const ctx = useTodoContext();
823
+ const { todos, filter } = useDataSubscribeMultiple(ctx, 'todos', 'filter');
824
+
825
+ const filteredTodos = useMemo(() => {
826
+ switch (filter) {
827
+ case 'active':
828
+ return todos.filter(todo => !todo.completed);
829
+ case 'completed':
830
+ return todos.filter(todo => todo.completed);
831
+ default:
832
+ return todos;
833
+ }
834
+ }, [todos, filter]);
835
+
836
+ return (
837
+ <ul className="todo-list">
838
+ {filteredTodos.map(todo => (
839
+ <TodoItem key={todo.id} todo={todo} />
840
+ ))}
841
+ </ul>
842
+ );
843
+ }
844
+
845
+ // Todo item component
846
+ function TodoItem({ todo }: { todo: Todo }) {
847
+ const ctx = useTodoContext();
848
+ const { toggleTodo } = useQuickSubscribe(ctx);
849
+
850
+ return (
851
+ <li className={`todo-item ${todo.completed ? 'completed' : ''}`}>
852
+ <input
853
+ type="checkbox"
854
+ checked={todo.completed}
855
+ onChange={() => toggleTodo(todo.id)}
856
+ />
857
+ <span>{todo.text}</span>
858
+ </li>
859
+ );
860
+ }
861
+
862
+ // Filter component
863
+ function TodoFilters() {
864
+ const ctx = useTodoContext();
865
+ const { filter, setFilter } = useQuickSubscribe(ctx);
866
+
867
+ return (
868
+ <div className="todo-filters">
869
+ {(['all', 'active', 'completed'] as const).map(filterType => (
870
+ <button
871
+ key={filterType}
872
+ className={filter === filterType ? 'active' : ''}
873
+ onClick={() => setFilter(filterType)}
874
+ >
875
+ {filterType}
876
+ </button>
877
+ ))}
878
+ </div>
879
+ );
880
+ }
881
+ ```
882
+
883
+ This comprehensive documentation covers all exported APIs from the react-state-custom library with detailed descriptions, type information, and practical examples.