@fukict/flux 0.1.0 → 0.1.2
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 +605 -0
- package/package.json +4 -1
package/README.md
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
# @fukict/flux
|
|
2
|
+
|
|
3
|
+
Minimal state management library for Fukict framework with Flux pattern and reactive subscriptions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Minimal API**: Simple `createFlux()` factory function
|
|
8
|
+
- **Reactive Subscriptions**: Subscribe to state changes with automatic updates
|
|
9
|
+
- **Selector Pattern**: Subscribe to derived/computed values
|
|
10
|
+
- **Type-Safe**: Full TypeScript support with type inference
|
|
11
|
+
- **Action Pattern**: Organized state mutations through actions
|
|
12
|
+
- **Dev Mode Protection**: Prevents direct state mutation in development
|
|
13
|
+
- **Zero Dependencies**: No external dependencies
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @fukict/flux
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Basic Counter Example
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Fukict } from '@fukict/basic';
|
|
27
|
+
import { createFlux } from '@fukict/flux';
|
|
28
|
+
|
|
29
|
+
// Create flux store
|
|
30
|
+
const counterFlux = createFlux({
|
|
31
|
+
state: {
|
|
32
|
+
count: 0,
|
|
33
|
+
},
|
|
34
|
+
actions: flux => ({
|
|
35
|
+
increment() {
|
|
36
|
+
const state = flux.getState();
|
|
37
|
+
flux.setState({ count: state.count + 1 });
|
|
38
|
+
},
|
|
39
|
+
decrement() {
|
|
40
|
+
const state = flux.getState();
|
|
41
|
+
flux.setState({ count: state.count - 1 });
|
|
42
|
+
},
|
|
43
|
+
setCount(value: number) {
|
|
44
|
+
flux.setState({ count: value });
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Use in component
|
|
50
|
+
class Counter extends Fukict {
|
|
51
|
+
private unsubscribe?: () => void;
|
|
52
|
+
|
|
53
|
+
mounted() {
|
|
54
|
+
// Subscribe to state changes
|
|
55
|
+
this.unsubscribe = counterFlux.subscribe(() => {
|
|
56
|
+
this.update(this.props);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
beforeUnmount() {
|
|
61
|
+
// Clean up subscription
|
|
62
|
+
this.unsubscribe?.();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
render() {
|
|
66
|
+
const state = counterFlux.getState();
|
|
67
|
+
const actions = counterFlux.getActions();
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div>
|
|
71
|
+
<p>Count: {state.count}</p>
|
|
72
|
+
<button on:click={actions.increment}>+</button>
|
|
73
|
+
<button on:click={actions.decrement}>-</button>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Core Concepts
|
|
81
|
+
|
|
82
|
+
### Creating Flux Store
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { createFlux } from '@fukict/flux';
|
|
86
|
+
|
|
87
|
+
interface TodoState {
|
|
88
|
+
todos: Todo[];
|
|
89
|
+
filter: 'all' | 'active' | 'completed';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const todoFlux = createFlux({
|
|
93
|
+
state: {
|
|
94
|
+
todos: [],
|
|
95
|
+
filter: 'all',
|
|
96
|
+
} as TodoState,
|
|
97
|
+
actions: flux => ({
|
|
98
|
+
addTodo(text: string) {
|
|
99
|
+
const state = flux.getState();
|
|
100
|
+
flux.setState({
|
|
101
|
+
todos: [...state.todos, { id: Date.now(), text, completed: false }],
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
toggleTodo(id: number) {
|
|
105
|
+
const state = flux.getState();
|
|
106
|
+
flux.setState({
|
|
107
|
+
todos: state.todos.map(todo =>
|
|
108
|
+
todo.id === id ? { ...todo, completed: !todo.completed } : todo,
|
|
109
|
+
),
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
setFilter(filter: TodoState['filter']) {
|
|
113
|
+
flux.setState({ filter });
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Subscribing to State
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
class TodoList extends Fukict {
|
|
123
|
+
private unsubscribe?: () => void;
|
|
124
|
+
|
|
125
|
+
mounted() {
|
|
126
|
+
// Subscribe to all state changes
|
|
127
|
+
this.unsubscribe = todoFlux.subscribe(() => {
|
|
128
|
+
this.update(this.props);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
beforeUnmount() {
|
|
133
|
+
this.unsubscribe?.();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
render() {
|
|
137
|
+
const { todos } = todoFlux.getState();
|
|
138
|
+
return (
|
|
139
|
+
<ul>
|
|
140
|
+
{todos.map(todo => (
|
|
141
|
+
<li key={todo.id}>{todo.text}</li>
|
|
142
|
+
))}
|
|
143
|
+
</ul>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Using Selectors
|
|
150
|
+
|
|
151
|
+
Selectors allow you to subscribe to derived/computed values:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// Define selector
|
|
155
|
+
const visibleTodosSelector = (state: TodoState) => {
|
|
156
|
+
switch (state.filter) {
|
|
157
|
+
case 'active':
|
|
158
|
+
return state.todos.filter(t => !t.completed);
|
|
159
|
+
case 'completed':
|
|
160
|
+
return state.todos.filter(t => t.completed);
|
|
161
|
+
default:
|
|
162
|
+
return state.todos;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
class FilteredTodoList extends Fukict {
|
|
167
|
+
private unsubscribe?: () => void;
|
|
168
|
+
|
|
169
|
+
mounted() {
|
|
170
|
+
// Subscribe to selector (only updates when selector result changes)
|
|
171
|
+
this.unsubscribe = todoFlux.subscribe(
|
|
172
|
+
visibleTodosSelector,
|
|
173
|
+
visibleTodos => {
|
|
174
|
+
console.log('Visible todos changed:', visibleTodos);
|
|
175
|
+
this.update(this.props);
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
beforeUnmount() {
|
|
181
|
+
this.unsubscribe?.();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
render() {
|
|
185
|
+
const visibleTodos = visibleTodosSelector(todoFlux.getState());
|
|
186
|
+
return (
|
|
187
|
+
<ul>
|
|
188
|
+
{visibleTodos.map(todo => (
|
|
189
|
+
<li key={todo.id}>{todo.text}</li>
|
|
190
|
+
))}
|
|
191
|
+
</ul>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Async Actions
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
const userFlux = createFlux({
|
|
201
|
+
state: {
|
|
202
|
+
user: null as User | null,
|
|
203
|
+
loading: false,
|
|
204
|
+
error: null as string | null,
|
|
205
|
+
},
|
|
206
|
+
actions: flux => ({
|
|
207
|
+
async login(email: string, password: string) {
|
|
208
|
+
flux.setState({ loading: true, error: null });
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const user = await api.login(email, password);
|
|
212
|
+
flux.setState({ user, loading: false });
|
|
213
|
+
} catch (error) {
|
|
214
|
+
flux.setState({
|
|
215
|
+
error: error.message,
|
|
216
|
+
loading: false,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
logout() {
|
|
221
|
+
flux.setState({ user: null });
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Subscription Patterns
|
|
228
|
+
|
|
229
|
+
### Pattern 1: Top-Level Subscription
|
|
230
|
+
|
|
231
|
+
Subscribe at the root/layout component level:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
class App extends Fukict {
|
|
235
|
+
private unsubscribe?: () => void;
|
|
236
|
+
|
|
237
|
+
mounted() {
|
|
238
|
+
// App subscribes, all children read state
|
|
239
|
+
this.unsubscribe = globalFlux.subscribe(() => {
|
|
240
|
+
this.update(this.props);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
beforeUnmount() {
|
|
245
|
+
this.unsubscribe?.();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
render() {
|
|
249
|
+
return (
|
|
250
|
+
<div>
|
|
251
|
+
<Header /> {/* Reads state, no subscription */}
|
|
252
|
+
<Content /> {/* Reads state, no subscription */}
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Pattern 2: Sibling Subscriptions
|
|
260
|
+
|
|
261
|
+
Different components subscribe to different stores:
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
class Dashboard extends Fukict {
|
|
265
|
+
render() {
|
|
266
|
+
return (
|
|
267
|
+
<div>
|
|
268
|
+
<CounterPanel /> {/* Subscribes to counterFlux */}
|
|
269
|
+
<TodoPanel /> {/* Subscribes to todoFlux */}
|
|
270
|
+
<UserPanel /> {/* Subscribes to userFlux */}
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Pattern 3: Detached Subscriptions
|
|
278
|
+
|
|
279
|
+
Use `fukict:detach` for independent updates:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
class Parent extends Fukict {
|
|
283
|
+
render() {
|
|
284
|
+
return (
|
|
285
|
+
<div>
|
|
286
|
+
<ChildA /> {/* Parent updates trigger ChildA update */}
|
|
287
|
+
<ChildB fukict:detach={true} /> {/* Independent updates */}
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## State Mutation Protection
|
|
295
|
+
|
|
296
|
+
In development mode, Flux prevents direct state mutation:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
const flux = createFlux({
|
|
300
|
+
state: { count: 0 },
|
|
301
|
+
actions: flux => ({
|
|
302
|
+
// ✅ Good: Use setState
|
|
303
|
+
increment() {
|
|
304
|
+
const state = flux.getState();
|
|
305
|
+
flux.setState({ count: state.count + 1 });
|
|
306
|
+
},
|
|
307
|
+
}),
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// ❌ Bad: Direct mutation (warning in dev mode)
|
|
311
|
+
const state = flux.getState();
|
|
312
|
+
state.count++; // Warning: [Flux] Direct state mutation is not allowed
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Advanced Usage
|
|
316
|
+
|
|
317
|
+
### Multiple Stores
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// User store
|
|
321
|
+
const userFlux = createFlux({
|
|
322
|
+
state: { user: null },
|
|
323
|
+
actions: (flux) => ({
|
|
324
|
+
setUser(user: User) {
|
|
325
|
+
flux.setState({ user });
|
|
326
|
+
},
|
|
327
|
+
}),
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Settings store
|
|
331
|
+
const settingsFlux = createFlux({
|
|
332
|
+
state: { theme: 'light', language: 'en' },
|
|
333
|
+
actions: (flux) => ({
|
|
334
|
+
setTheme(theme: string) {
|
|
335
|
+
flux.setState({ theme });
|
|
336
|
+
},
|
|
337
|
+
setLanguage(language: string) {
|
|
338
|
+
flux.setState({ language });
|
|
339
|
+
},
|
|
340
|
+
}),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Use both in component
|
|
344
|
+
class Profile extends Fukict {
|
|
345
|
+
private unsubscribeUser?: () => void;
|
|
346
|
+
private unsubscribeSettings?: () => void;
|
|
347
|
+
|
|
348
|
+
mounted() {
|
|
349
|
+
this.unsubscribeUser = userFlux.subscribe(() => this.update(this.props));
|
|
350
|
+
this.unsubscribeSettings = settingsFlux.subscribe(() =>
|
|
351
|
+
this.update(this.props)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
beforeUnmount() {
|
|
356
|
+
this.unsubscribeUser?.();
|
|
357
|
+
this.unsubscribeSettings?.();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
render() {
|
|
361
|
+
const { user } = userFlux.getState();
|
|
362
|
+
const { theme } = settingsFlux.getState();
|
|
363
|
+
return <div class={theme}>{user?.name}</div>;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Computed Values with Selectors
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const statsSelector = (state: TodoState) => ({
|
|
372
|
+
total: state.todos.length,
|
|
373
|
+
completed: state.todos.filter((t) => t.completed).length,
|
|
374
|
+
active: state.todos.filter((t) => !t.completed).length,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
class TodoStats extends Fukict {
|
|
378
|
+
private unsubscribe?: () => void;
|
|
379
|
+
|
|
380
|
+
mounted() {
|
|
381
|
+
this.unsubscribe = todoFlux.subscribe(statsSelector, (stats) => {
|
|
382
|
+
console.log('Stats changed:', stats);
|
|
383
|
+
this.update(this.props);
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
beforeUnmount() {
|
|
388
|
+
this.unsubscribe?.();
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
render() {
|
|
392
|
+
const stats = statsSelector(todoFlux.getState());
|
|
393
|
+
return (
|
|
394
|
+
<div>
|
|
395
|
+
<p>Total: {stats.total}</p>
|
|
396
|
+
<p>Completed: {stats.completed}</p>
|
|
397
|
+
<p>Active: {stats.active}</p>
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Store Composition
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
const createAppFlux = () => {
|
|
408
|
+
const userFlux = createFlux({
|
|
409
|
+
state: { user: null },
|
|
410
|
+
actions: (flux) => ({ /*...*/ }),
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const todoFlux = createFlux({
|
|
414
|
+
state: { todos: [] },
|
|
415
|
+
actions: (flux) => ({ /*...*/ }),
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
user: userFlux,
|
|
420
|
+
todo: todoFlux,
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const appFlux = createAppFlux();
|
|
425
|
+
|
|
426
|
+
// Use in components
|
|
427
|
+
appFlux.user.getState();
|
|
428
|
+
appFlux.user.getActions().setUser(...);
|
|
429
|
+
appFlux.todo.getState();
|
|
430
|
+
appFlux.todo.getActions().addTodo(...);
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## API Reference
|
|
434
|
+
|
|
435
|
+
### createFlux(config)
|
|
436
|
+
|
|
437
|
+
Creates a new flux store.
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
const flux = createFlux({
|
|
441
|
+
state: initialState,
|
|
442
|
+
actions: flux => ({
|
|
443
|
+
actionName(...args) {
|
|
444
|
+
// Action implementation
|
|
445
|
+
flux.setState(newState);
|
|
446
|
+
},
|
|
447
|
+
}),
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### flux.getState()
|
|
452
|
+
|
|
453
|
+
Returns current state (protected from mutation in dev mode).
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
const state = flux.getState();
|
|
457
|
+
console.log(state.count);
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### flux.setState(partial)
|
|
461
|
+
|
|
462
|
+
Updates state with partial state object.
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
flux.setState({ count: 10 });
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### flux.getActions()
|
|
469
|
+
|
|
470
|
+
Returns actions object.
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
const actions = flux.getActions();
|
|
474
|
+
actions.increment();
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### flux.subscribe(listener)
|
|
478
|
+
|
|
479
|
+
Subscribes to all state changes.
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
const unsubscribe = flux.subscribe(() => {
|
|
483
|
+
console.log('State changed');
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// Clean up
|
|
487
|
+
unsubscribe();
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### flux.subscribe(selector, listener)
|
|
491
|
+
|
|
492
|
+
Subscribes to selector changes (only triggers when selector result changes).
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
const unsubscribe = flux.subscribe(
|
|
496
|
+
state => state.user.name,
|
|
497
|
+
name => {
|
|
498
|
+
console.log('Name changed:', name);
|
|
499
|
+
},
|
|
500
|
+
);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Best Practices
|
|
504
|
+
|
|
505
|
+
### 1. Organize Actions by Feature
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// ✅ Good: Group related actions
|
|
509
|
+
const userFlux = createFlux({
|
|
510
|
+
state: { user: null, preferences: {} },
|
|
511
|
+
actions: flux => ({
|
|
512
|
+
// Auth actions
|
|
513
|
+
login(credentials) {
|
|
514
|
+
/*...*/
|
|
515
|
+
},
|
|
516
|
+
logout() {
|
|
517
|
+
/*...*/
|
|
518
|
+
},
|
|
519
|
+
|
|
520
|
+
// Preference actions
|
|
521
|
+
updatePreference(key, value) {
|
|
522
|
+
/*...*/
|
|
523
|
+
},
|
|
524
|
+
resetPreferences() {
|
|
525
|
+
/*...*/
|
|
526
|
+
},
|
|
527
|
+
}),
|
|
528
|
+
});
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### 2. Use Selectors for Derived State
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// ✅ Good: Selector for computed values
|
|
535
|
+
const expensiveSelector = (state) => {
|
|
536
|
+
return state.items.filter(/*...*/).map(/*...*/).reduce(/*...*/);
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// ❌ Bad: Compute in render
|
|
540
|
+
render() {
|
|
541
|
+
const state = flux.getState();
|
|
542
|
+
const result = state.items.filter(/*...*/).map(/*...*/).reduce(/*...*/);
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### 3. Clean Up Subscriptions
|
|
547
|
+
|
|
548
|
+
```tsx
|
|
549
|
+
// ✅ Good: Always clean up
|
|
550
|
+
class MyComponent extends Fukict {
|
|
551
|
+
private unsubscribe?: () => void;
|
|
552
|
+
|
|
553
|
+
mounted() {
|
|
554
|
+
this.unsubscribe = flux.subscribe(() => this.update(this.props));
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
beforeUnmount() {
|
|
558
|
+
this.unsubscribe?.(); // Essential!
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### 4. Subscribe at the Right Level
|
|
564
|
+
|
|
565
|
+
```tsx
|
|
566
|
+
// ✅ Good: Subscribe at parent, read in children
|
|
567
|
+
class App extends Fukict {
|
|
568
|
+
mounted() {
|
|
569
|
+
this.unsubscribe = flux.subscribe(() => this.update(this.props));
|
|
570
|
+
}
|
|
571
|
+
render() {
|
|
572
|
+
return (
|
|
573
|
+
<div>
|
|
574
|
+
<Child />
|
|
575
|
+
</div>
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
class Child extends Fukict {
|
|
581
|
+
render() {
|
|
582
|
+
const state = flux.getState(); // Just read
|
|
583
|
+
return <div>{state.value}</div>;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Examples
|
|
589
|
+
|
|
590
|
+
See [examples/infra-flux](../../examples/infra-flux) for complete examples:
|
|
591
|
+
|
|
592
|
+
- Counter with async actions
|
|
593
|
+
- Todo list with filters
|
|
594
|
+
- User profile with settings
|
|
595
|
+
- Selector patterns
|
|
596
|
+
|
|
597
|
+
## Related Packages
|
|
598
|
+
|
|
599
|
+
- [@fukict/basic](../basic) - Core rendering engine
|
|
600
|
+
- [@fukict/router](../router) - SPA routing
|
|
601
|
+
- [@fukict/i18n](../i18n) - Internationalization
|
|
602
|
+
|
|
603
|
+
## License
|
|
604
|
+
|
|
605
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fukict/flux",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Minimal state management library for Fukict framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fukict",
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=16.0.0"
|
|
43
43
|
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"registry": "https://registry.npmjs.org/"
|
|
46
|
+
},
|
|
44
47
|
"scripts": {
|
|
45
48
|
"build": "tsx ../../scripts/build-package.ts --pkg-name flux --no-watch",
|
|
46
49
|
"dev": "tsx ../../scripts/build-package.ts --pkg-name flux --watch",
|