@wirestate/react 0.6.2 → 0.6.3
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/CHANGELOG.md +4 -0
- package/README.md +114 -234
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,319 +1,199 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wirestate/react
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Neloreck/wirestate/search?l=typescript)
|
|
3
|
+
[](https://www.npmjs.com/package/@wirestate/react)
|
|
5
4
|
[](https://github.com/Neloreck/wirestate/blob/master/LICENSE)
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
providing IOC/DI/indirection based architecture based on concepts of Services, Events, Commands, and Queries.
|
|
9
|
-
|
|
10
|
-
It optionally works with **MobX**, **Signals**, or other custom reactivity implementations.
|
|
11
|
-
|
|
12
|
-
## Architecture & Core Concepts
|
|
13
|
-
|
|
14
|
-
Designed for complex applications, `wirestate` enforces a clear separation of concerns:
|
|
15
|
-
|
|
16
|
-
- **Services**: Singleton-scoped logic units that hold state and business logic.
|
|
17
|
-
- **Dependency Injection**: First-class InversifyJS integration for decoupled, testable code.
|
|
18
|
-
- **Reactivity**: Independent state tracking (optional integration with MobX, Signals, etc.).
|
|
19
|
-
- **Events**: Fire-and-forget communication for cross-service side effects.
|
|
20
|
-
- **Commands**: Encapsulated write operations with standardized execution status (pending, settled, error).
|
|
21
|
-
- **Queries**: Synchronous or asynchronous request-response patterns for data retrieval.
|
|
22
|
-
- **Lifecycle Management**: Automated services provision within react tree, activation/deactivation lifecycle.
|
|
23
|
-
|
|
24
|
-
## Requirements
|
|
25
|
-
|
|
26
|
-
- `react >= 16.8.0`
|
|
27
|
-
- `reflect-metadata` (must be imported at application entry)
|
|
6
|
+
React integration for wirestate. Providers and hooks for injecting services and communicating through events, commands, and queries.
|
|
28
7
|
|
|
29
8
|
## Installation
|
|
30
9
|
|
|
31
10
|
```bash
|
|
32
|
-
npm install
|
|
11
|
+
npm install @wirestate/core @wirestate/react reflect-metadata
|
|
33
12
|
```
|
|
34
13
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npm install --save @wirestate/react @wirestate/react-mobx mobx mobx-react-lite
|
|
39
|
-
```
|
|
14
|
+
## Providers
|
|
40
15
|
|
|
41
|
-
###
|
|
16
|
+
### `IocProvider`
|
|
42
17
|
|
|
43
|
-
|
|
44
|
-
npm install --save @wirestate/react @wirestate/react-signals @preact/signals-react
|
|
45
|
-
npm install --save-dev @preact/signals-react-transform
|
|
46
|
-
```
|
|
18
|
+
Root provider. Creates the top-level IoC container. Place once near the root of your application.
|
|
47
19
|
|
|
48
|
-
|
|
20
|
+
```tsx
|
|
21
|
+
import { IocProvider } from '@wirestate/react';
|
|
49
22
|
|
|
50
|
-
|
|
23
|
+
export function App() {
|
|
24
|
+
return (
|
|
25
|
+
<IocProvider>
|
|
26
|
+
<SomeComponent />
|
|
27
|
+
</IocProvider>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
51
31
|
|
|
52
|
-
|
|
32
|
+
### `createInjectablesProvider`
|
|
53
33
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
import { makeObservable, Observable, Action } from '@wirestate/react-mobx';
|
|
34
|
+
Creates a component that binds a set of services into a child container scoped to the component's lifetime.
|
|
35
|
+
Services are activated on mount and deactivated on unmount. Expects to be under `IocProvider` tree.
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
public count: number = 0;
|
|
37
|
+
```tsx
|
|
38
|
+
import { createInjectablesProvider } from '@wirestate/react';
|
|
39
|
+
import { CounterService, LoggerService } from './services';
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
@Inject(WireScope)
|
|
65
|
-
private scope: WireScope
|
|
66
|
-
) {
|
|
67
|
-
makeObservable(this);
|
|
68
|
-
}
|
|
41
|
+
const InjectionProvider = createInjectablesProvider([CounterService, LoggerService]);
|
|
69
42
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
43
|
+
export function CounterPage() {
|
|
44
|
+
return (
|
|
45
|
+
<InjectionProvider>
|
|
46
|
+
<CounterView />
|
|
47
|
+
</InjectionProvider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
74
51
|
|
|
75
|
-
|
|
76
|
-
public reset(): void {
|
|
77
|
-
this.count = 0;
|
|
78
|
-
}
|
|
52
|
+
**Props:**
|
|
79
53
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
54
|
+
| Prop | Type | Description |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `seed` | `object` | Shared seed injected into all services via `@Inject(SEED)` |
|
|
57
|
+
| `seeds` | `SeedEntries` | Per-service seeds, e.g. `[[CounterService, { count: 10 }]]` |
|
|
84
58
|
|
|
85
|
-
|
|
86
|
-
public onGetTotal(): number {
|
|
87
|
-
return this.count;
|
|
88
|
-
}
|
|
59
|
+
Both `seed` and `seeds` are applied on first render only.
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
public onResetEvent(): void {
|
|
92
|
-
this.reset();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
61
|
+
## Injection hooks
|
|
96
62
|
|
|
97
|
-
###
|
|
63
|
+
### `useInjection(token)`
|
|
98
64
|
|
|
99
|
-
|
|
100
|
-
Lifetimes are managed automatically.
|
|
65
|
+
Resolves a value from the nearest container. Re-resolves when the container resets.
|
|
101
66
|
|
|
102
67
|
```tsx
|
|
103
|
-
import {
|
|
104
|
-
import { CounterService } from './
|
|
68
|
+
import { useInjection } from '@wirestate/react';
|
|
69
|
+
import { CounterService } from './services';
|
|
105
70
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<IocProvider>
|
|
111
|
-
<MainProvider>
|
|
112
|
-
<CounterView />
|
|
113
|
-
</MainProvider>
|
|
114
|
-
</IocProvider>
|
|
115
|
-
);
|
|
71
|
+
function CounterView() {
|
|
72
|
+
const counter = useInjection(CounterService);
|
|
73
|
+
return <span>{counter.count}</span>;
|
|
116
74
|
}
|
|
117
75
|
```
|
|
118
76
|
|
|
119
|
-
###
|
|
77
|
+
### `useOptionalInjection(token)`
|
|
120
78
|
|
|
121
|
-
|
|
122
|
-
Or use specialized hooks for communication without direct references.
|
|
79
|
+
Same as `useInjection` but returns `null` if the token is not bound.
|
|
123
80
|
|
|
124
|
-
|
|
125
|
-
import { useInjection, useCommandCaller, useEventEmitter } from '@wirestate/react';
|
|
126
|
-
import { observer } from '@wirestate/react-mobx';
|
|
127
|
-
import { CounterService } from './CounterService';
|
|
81
|
+
## Event hooks
|
|
128
82
|
|
|
129
|
-
|
|
130
|
-
const service = useInjection(CounterService);
|
|
131
|
-
const call = useCommandCaller();
|
|
132
|
-
const emit = useEventEmitter();
|
|
83
|
+
### `useEventEmitter()`
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
<div>
|
|
136
|
-
<p>Count: {service.count}</p>
|
|
137
|
-
<button onClick={() => service.increment(5)}>Add 1 (Method)</button>
|
|
138
|
-
<button onClick={() => call('INCREMENT', 5)}>Add 5 (Command)</button>
|
|
139
|
-
<button onClick={() => service.reset()}>Reset (Method)</button>
|
|
140
|
-
<button onClick={() => emit('RESET')}>Reset (Event)</button>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
```
|
|
85
|
+
Returns a function that emits an event into the current container's event bus.
|
|
145
86
|
|
|
146
|
-
|
|
87
|
+
```tsx
|
|
88
|
+
const emit = useEventEmitter();
|
|
147
89
|
|
|
148
|
-
|
|
90
|
+
emit('RESET');
|
|
91
|
+
emit('ADD', { amount: 5 });
|
|
92
|
+
```
|
|
149
93
|
|
|
150
|
-
|
|
94
|
+
### `useEvent(type, handler)`
|
|
151
95
|
|
|
152
|
-
|
|
153
|
-
import { Injectable, Inject, WireScope, OnEvent, OnCommand, OnQuery } from '@wirestate/core';
|
|
154
|
-
import { signal, computed, Signal, ReadonlySignal } from '@wirestate/react-signals';
|
|
96
|
+
Subscribes a handler to a single event type for the lifetime of the component.
|
|
155
97
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
98
|
+
```tsx
|
|
99
|
+
useEvent('RESET', (event) => {
|
|
100
|
+
console.log(event.type, event.payload);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
160
103
|
|
|
161
|
-
|
|
162
|
-
@Inject(WireScope)
|
|
163
|
-
private scope: WireScope
|
|
164
|
-
) {}
|
|
104
|
+
### `useEvents(types, handler)`
|
|
165
105
|
|
|
166
|
-
|
|
167
|
-
this.count.value += amount;
|
|
168
|
-
}
|
|
106
|
+
Subscribes to multiple event types with a single handler.
|
|
169
107
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
108
|
+
```tsx
|
|
109
|
+
useEvents(['RESET', 'CLEAR'], (event) => {
|
|
110
|
+
console.log(event.type, event.payload);
|
|
111
|
+
});
|
|
112
|
+
```
|
|
173
113
|
|
|
174
|
-
|
|
175
|
-
public onIncrementCommand(amount: number = 1): void {
|
|
176
|
-
this.increment(amount);
|
|
177
|
-
}
|
|
114
|
+
### `useEventsHandler(handler)`
|
|
178
115
|
|
|
179
|
-
|
|
180
|
-
public onGetTotal(): number {
|
|
181
|
-
return this.count.value;
|
|
182
|
-
}
|
|
116
|
+
Subscribes to all events. The handler receives the event type and payload.
|
|
183
117
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
118
|
+
```tsx
|
|
119
|
+
useEventsHandler((event) => {
|
|
120
|
+
logger.log(event.type, event.payload);
|
|
121
|
+
});
|
|
189
122
|
```
|
|
190
123
|
|
|
191
|
-
|
|
124
|
+
## Command hooks
|
|
192
125
|
|
|
193
|
-
|
|
126
|
+
### `useCommandCaller()`
|
|
194
127
|
|
|
195
|
-
|
|
196
|
-
import { IocProvider, createInjectablesProvider } from '@wirestate/react';
|
|
197
|
-
import { CounterService } from './CounterService';
|
|
128
|
+
Returns a function that dispatches a command and waits for its result.
|
|
198
129
|
|
|
199
|
-
|
|
130
|
+
```tsx
|
|
131
|
+
const call = useCommandCaller();
|
|
200
132
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<IocProvider>
|
|
204
|
-
<MainProvider>
|
|
205
|
-
<CounterView />
|
|
206
|
-
</MainProvider>
|
|
207
|
-
</IocProvider>
|
|
208
|
-
);
|
|
133
|
+
async function handleClick() {
|
|
134
|
+
await call('LOGIN', { username: 'alice' }).task;
|
|
209
135
|
}
|
|
210
136
|
```
|
|
211
137
|
|
|
212
|
-
###
|
|
138
|
+
### `useOptionalCommandCaller()`
|
|
213
139
|
|
|
214
|
-
|
|
140
|
+
Same as `useCommandCaller` but returns `null` if no handler is registered.
|
|
215
141
|
|
|
216
|
-
|
|
217
|
-
import { useInjection, useCommandCaller, useEventEmitter } from '@wirestate/react';
|
|
218
|
-
import { CounterService } from './CounterService';
|
|
142
|
+
### `useCommandHandler(type, handler)`
|
|
219
143
|
|
|
220
|
-
|
|
221
|
-
const service = useInjection(CounterService);
|
|
222
|
-
const call = useCommandCaller();
|
|
223
|
-
const emit = useEventEmitter();
|
|
144
|
+
Registers a command handler from a component for the lifetime of the component.
|
|
224
145
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
<button onClick={() => service.increment(5)}>Add 1 (Method)</button>
|
|
230
|
-
<button onClick={() => call('INCREMENT', 5)}>Add 5 (Command)</button>
|
|
231
|
-
<button onClick={() => service.reset()}>Reset (Method)</button>
|
|
232
|
-
<button onClick={() => emit('RESET')}>Reset (Event)</button>
|
|
233
|
-
</div>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
146
|
+
```tsx
|
|
147
|
+
useCommandHandler('SCROLL_TOP', () => {
|
|
148
|
+
window.scrollTo(0, 0);
|
|
149
|
+
});
|
|
236
150
|
```
|
|
237
151
|
|
|
238
|
-
##
|
|
152
|
+
## Query hooks
|
|
239
153
|
|
|
240
|
-
###
|
|
154
|
+
### `useQueryCaller()`
|
|
241
155
|
|
|
242
|
-
|
|
156
|
+
Returns an async function that calls a query handler and resolves its return value.
|
|
243
157
|
|
|
244
158
|
```tsx
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
<IocProvider>
|
|
248
|
-
<MainProvider seed={{ initialCount: 100 }}>
|
|
249
|
-
<CounterView />
|
|
250
|
-
</MainProvider>
|
|
251
|
-
</IocProvider>
|
|
159
|
+
const query = useQueryCaller();
|
|
160
|
+
const items = await query('GET_ITEMS');
|
|
252
161
|
```
|
|
253
162
|
|
|
254
|
-
|
|
163
|
+
### `useSyncQueryCaller()`
|
|
255
164
|
|
|
256
|
-
|
|
257
|
-
import { Injectable, Inject, SEED } from 'wirestate';
|
|
165
|
+
Same as `useQueryCaller` but calls a synchronous handler.
|
|
258
166
|
|
|
259
|
-
|
|
260
|
-
export class CounterService {
|
|
261
|
-
// ...
|
|
262
|
-
public constructor(
|
|
263
|
-
@Inject(SEED)
|
|
264
|
-
initialState: { initialCount: number }
|
|
265
|
-
) {
|
|
266
|
-
this.count = seed.initialCount;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
```
|
|
167
|
+
### `useOptionalQueryCaller()` / `useOptionalSyncQueryCaller()`
|
|
270
168
|
|
|
271
|
-
|
|
169
|
+
Return `null` when no handler is registered instead of throwing.
|
|
272
170
|
|
|
273
|
-
`
|
|
171
|
+
### `useQueryHandler(type, handler)`
|
|
274
172
|
|
|
275
|
-
|
|
276
|
-
const MainProvider = createInjectablesProvider([CounterService]);
|
|
173
|
+
Registers a query handler from a component.
|
|
277
174
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
<CounterView />
|
|
281
|
-
</MainProvider>
|
|
282
|
-
</IocProvider>
|
|
175
|
+
```tsx
|
|
176
|
+
useQueryHandler('GET_SCROLL_POS', () => window.scrollY);
|
|
283
177
|
```
|
|
284
178
|
|
|
285
|
-
|
|
179
|
+
## Container hooks
|
|
286
180
|
|
|
287
|
-
|
|
288
|
-
import { Injectable, Inject, WireScope } from 'wirestate';
|
|
289
|
-
|
|
290
|
-
@Injectable()
|
|
291
|
-
export class CounterService {
|
|
292
|
-
// ...
|
|
293
|
-
public constructor(@Inject(WireScope) scope: WireScope) {
|
|
294
|
-
this.count = scope.getSeed(CounterService).count;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
```
|
|
181
|
+
### `useContainer()`
|
|
298
182
|
|
|
299
|
-
|
|
183
|
+
Returns the nearest `Container` instance. Useful for advanced manual resolution.
|
|
300
184
|
|
|
301
|
-
|
|
185
|
+
### `useContainerRevision()`
|
|
302
186
|
|
|
303
|
-
|
|
304
|
-
import { OnActivated, OnDeactivation } from 'wirestate';
|
|
187
|
+
Returns a counter that increments each time the container is reset. Use to trigger effects when the container changes.
|
|
305
188
|
|
|
306
|
-
|
|
307
|
-
public onActivated(): void {
|
|
308
|
-
// Start polling, fetch initial data, etc.
|
|
309
|
-
}
|
|
189
|
+
## Test utilities
|
|
310
190
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// Cleanup subscriptions
|
|
314
|
-
}
|
|
191
|
+
```ts
|
|
192
|
+
import { withIocProvider } from '@wirestate/react/test-utils';
|
|
315
193
|
```
|
|
316
194
|
|
|
195
|
+
`withIocProvider(children, container?, seed?)` — wraps a React tree with an `IocProvider` for use in tests.
|
|
196
|
+
|
|
317
197
|
## License
|
|
318
198
|
|
|
319
199
|
MIT
|