react-state-custom 1.0.11 → 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.
- package/API_DOCUMENTATION.md +883 -0
- package/README.md +180 -40
- package/package.json +1 -1
|
@@ -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.
|
package/README.md
CHANGED
|
@@ -1,57 +1,197 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React State Custom - Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Welcome to the comprehensive documentation for the `react-state-custom` library!
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 📚 Documentation Files
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference with examples for all exported functions, hooks, and classes
|
|
8
|
+
|
|
9
|
+
## 🚀 Quick Start
|
|
8
10
|
|
|
9
11
|
```bash
|
|
10
|
-
|
|
12
|
+
npm install react-state-custom
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
## 📖 What's Inside
|
|
16
|
+
|
|
17
|
+
The `react-state-custom` library provides a powerful set of tools for managing shared state in React applications:
|
|
18
|
+
|
|
19
|
+
### Core Features
|
|
20
|
+
|
|
21
|
+
- **Context System** - Type-safe context management with event-driven subscriptions
|
|
22
|
+
- **Root Context Factory** - Automated context lifecycle management
|
|
23
|
+
- **Auto Context System** - Self-managing context instances
|
|
24
|
+
- **Utility Hooks** - Performance optimization tools
|
|
25
|
+
|
|
26
|
+
### Key Benefits
|
|
27
|
+
|
|
28
|
+
- ✅ **Type Safety** - Full TypeScript support with strong typing
|
|
29
|
+
- ✅ **Performance** - Only re-renders when subscribed data changes
|
|
30
|
+
- ✅ **Flexibility** - Works with any data structure
|
|
31
|
+
- ✅ **Developer Experience** - Rich debugging and error checking
|
|
32
|
+
- ✅ **Minimal Boilerplate** - Automated context management
|
|
33
|
+
|
|
34
|
+
## 📝 Documentation Structure
|
|
35
|
+
|
|
36
|
+
The [API Documentation](./API_DOCUMENTATION.md) is organized into the following sections:
|
|
37
|
+
|
|
38
|
+
1. **Core Context System** - Basic context functionality
|
|
39
|
+
2. **Data Source Hooks** - Publishing data to contexts
|
|
40
|
+
3. **Data Subscription Hooks** - Subscribing to context changes
|
|
41
|
+
4. **Root Context Factory** - Advanced context patterns
|
|
42
|
+
5. **Auto Context System** - Automated context management
|
|
43
|
+
6. **Utility Hooks** - Performance and utility functions
|
|
44
|
+
7. **Usage Patterns** - Common implementation patterns
|
|
45
|
+
8. **Examples** - Complete application examples
|
|
46
|
+
|
|
47
|
+
## 🎯 Common Use Cases
|
|
48
|
+
|
|
49
|
+
- **Global State Management** - Application-wide state without Redux complexity
|
|
50
|
+
- **Component Communication** - Share data between distant components
|
|
51
|
+
- **Performance Optimization** - Minimize unnecessary re-renders
|
|
52
|
+
- **Context Composition** - Combine multiple contexts efficiently
|
|
53
|
+
|
|
54
|
+
## 🔧 Quick Example
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { useDataContext, useDataSource, useDataSubscribe } from 'react-state-custom';
|
|
58
|
+
|
|
59
|
+
interface AppState {
|
|
60
|
+
user: User | null;
|
|
61
|
+
theme: 'light' | 'dark';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Provider component
|
|
65
|
+
function AppProvider({ children }) {
|
|
66
|
+
const ctx = useDataContext<AppState>('app-state');
|
|
67
|
+
const user = useCurrentUser();
|
|
68
|
+
const theme = useTheme();
|
|
69
|
+
|
|
70
|
+
useDataSource(ctx, 'user', user);
|
|
71
|
+
useDataSource(ctx, 'theme', theme);
|
|
72
|
+
|
|
73
|
+
return <>{children}</>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Consumer component
|
|
77
|
+
function UserProfile() {
|
|
78
|
+
const ctx = useDataContext<AppState>('app-state');
|
|
79
|
+
const user = useDataSubscribe(ctx, 'user');
|
|
80
|
+
|
|
81
|
+
return <div>Hello, {user?.name}</div>;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
19
84
|
|
|
20
|
-
|
|
85
|
+
## 🔧 Additional Examples
|
|
86
|
+
|
|
87
|
+
### Using createRootCtx for Advanced State Management
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { createRootCtx, useDataSubscribeMultiple } from 'react-state-custom';
|
|
91
|
+
|
|
92
|
+
interface UserState {
|
|
93
|
+
user: User | null;
|
|
94
|
+
loading: boolean;
|
|
95
|
+
error: string | null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Create a state hook
|
|
99
|
+
function useUserState(props: { userId: string }) {
|
|
100
|
+
const [user, setUser] = useState<User | null>(null);
|
|
101
|
+
const [loading, setLoading] = useState(true);
|
|
102
|
+
const [error, setError] = useState<string | null>(null);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
fetchUser(props.userId).then(setUser).catch(setError).finally(() => setLoading(false));
|
|
106
|
+
}, [props.userId]);
|
|
107
|
+
|
|
108
|
+
return { user, loading, error };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create root context factory
|
|
112
|
+
const { Root: UserRoot, useCtxState: useUserCtxState } = createRootCtx(
|
|
113
|
+
'user-state',
|
|
114
|
+
useUserState
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Provider component
|
|
118
|
+
function UserProvider({ userId, children }: { userId: string; children: React.ReactNode }) {
|
|
21
119
|
return (
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
120
|
+
<>
|
|
121
|
+
<UserRoot userId={userId} />
|
|
122
|
+
{children}
|
|
123
|
+
</>
|
|
25
124
|
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Consumer component using useDataSubscribeMultiple
|
|
128
|
+
function UserProfile({ userId }: { userId: string }) {
|
|
129
|
+
const ctx = useUserCtxState({ userId });
|
|
130
|
+
const { user, loading, error } = useDataSubscribeMultiple(ctx, 'user', 'loading', 'error');
|
|
131
|
+
|
|
132
|
+
if (loading) return <div>Loading user...</div>;
|
|
133
|
+
if (error) return <div>Error: {error}</div>;
|
|
134
|
+
|
|
135
|
+
return <div>Welcome, {user?.name}!</div>;
|
|
136
|
+
}
|
|
29
137
|
```
|
|
30
138
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
139
|
+
### Using useQuickSubscribe for Simplified Access
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { useDataContext, useQuickSubscribe } from 'react-state-custom';
|
|
143
|
+
|
|
144
|
+
interface SettingsState {
|
|
145
|
+
theme: 'light' | 'dark';
|
|
146
|
+
language: string;
|
|
147
|
+
notifications: boolean;
|
|
148
|
+
updateSetting: (key: string, value: any) => void;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Component using useQuickSubscribe for easy property access
|
|
152
|
+
function SettingsPanel() {
|
|
153
|
+
const ctx = useDataContext<SettingsState>('settings');
|
|
154
|
+
|
|
155
|
+
// useQuickSubscribe automatically subscribes to accessed properties
|
|
156
|
+
const { theme, language, notifications, updateSetting } = useQuickSubscribe(ctx);
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div className={`settings-panel ${theme}`}>
|
|
160
|
+
<h2>Settings</h2>
|
|
161
|
+
|
|
162
|
+
<label>
|
|
163
|
+
Theme:
|
|
164
|
+
<select value={theme} onChange={(e) => updateSetting('theme', e.target.value)}>
|
|
165
|
+
<option value="light">Light</option>
|
|
166
|
+
<option value="dark">Dark</option>
|
|
167
|
+
</select>
|
|
168
|
+
</label>
|
|
169
|
+
|
|
170
|
+
<label>
|
|
171
|
+
Language:
|
|
172
|
+
<input
|
|
173
|
+
value={language}
|
|
174
|
+
onChange={(e) => updateSetting('language', e.target.value)}
|
|
175
|
+
/>
|
|
176
|
+
</label>
|
|
177
|
+
|
|
178
|
+
<label>
|
|
179
|
+
<input
|
|
180
|
+
type="checkbox"
|
|
181
|
+
checked={notifications}
|
|
182
|
+
onChange={(e) => updateSetting('notifications', e.target.checked)}
|
|
183
|
+
/>
|
|
184
|
+
Enable notifications
|
|
185
|
+
</label>
|
|
186
|
+
</div>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
45
189
|
```
|
|
46
190
|
|
|
47
|
-
##
|
|
48
|
-
|
|
49
|
-
To build the library for production, run:
|
|
191
|
+
## 📄 License
|
|
50
192
|
|
|
51
|
-
|
|
52
|
-
yarn build
|
|
53
|
-
```
|
|
193
|
+
MIT License - see the main repository for details.
|
|
54
194
|
|
|
55
|
-
|
|
195
|
+
---
|
|
56
196
|
|
|
57
|
-
|
|
197
|
+
For the complete API reference with detailed examples, see [API_DOCUMENTATION.md](./API_DOCUMENTATION.md).
|