@wirestate/react-signals 0.6.1 → 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 +8 -0
- package/README.md +20 -286
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,156 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @wirestate/react-signals
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Neloreck/wirestate/search?l=typescript)
|
|
3
|
+
[](https://www.npmjs.com/package/@wirestate/react-signals)
|
|
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
|
+
Re-exports `@preact/signals-react` for use with wirestate services.
|
|
28
7
|
|
|
29
8
|
## Installation
|
|
30
9
|
|
|
31
10
|
```bash
|
|
32
|
-
npm install
|
|
11
|
+
npm install @wirestate/react-signals @preact/signals-react
|
|
33
12
|
```
|
|
34
13
|
|
|
35
|
-
|
|
14
|
+
For automatic component subscription without manual `useSignal` calls, add the Babel or SWC transform:
|
|
36
15
|
|
|
37
16
|
```bash
|
|
38
|
-
npm install --save @wirestate/react @wirestate/react-mobx mobx mobx-react-lite
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### For signals
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install --save @wirestate/react @wirestate/react-signals @preact/signals-react
|
|
45
17
|
npm install --save-dev @preact/signals-react-transform
|
|
46
18
|
```
|
|
47
19
|
|
|
48
|
-
##
|
|
49
|
-
|
|
50
|
-
### 1. Define a Service
|
|
51
|
-
|
|
52
|
-
Services are standard classes decorated with `@Injectable`. Use `WireScope` to interact with the framework.
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
import { Injectable, Inject, WireScope, OnEvent, OnCommand, OnQuery } from '@wirestate/core';
|
|
56
|
-
import { makeObservable, Observable, Action } from '@wirestate/react-mobx';
|
|
57
|
-
|
|
58
|
-
@Injectable()
|
|
59
|
-
export class CounterService {
|
|
60
|
-
@Observable()
|
|
61
|
-
public count: number = 0;
|
|
62
|
-
|
|
63
|
-
public constructor(
|
|
64
|
-
@Inject(WireScope)
|
|
65
|
-
private scope: WireScope
|
|
66
|
-
) {
|
|
67
|
-
makeObservable(this);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
@Action()
|
|
71
|
-
public increment(amount: number = 1): void {
|
|
72
|
-
this.count += amount;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@Action()
|
|
76
|
-
public reset(): void {
|
|
77
|
-
this.count = 0;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@OnCommand('INCREMENT')
|
|
81
|
-
public onIncrementCommand(amount: number = 1): void {
|
|
82
|
-
this.increment(amount);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@OnQuery('GET_TOTAL')
|
|
86
|
-
public onGetTotal(): number {
|
|
87
|
-
return this.count;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@OnEvent('RESET')
|
|
91
|
-
public onResetEvent(): void {
|
|
92
|
-
this.reset();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### 2. Configure the Provider
|
|
98
|
-
|
|
99
|
-
Bind services at any level of the component tree.
|
|
100
|
-
Lifetimes are managed automatically.
|
|
20
|
+
## Usage
|
|
101
21
|
|
|
102
|
-
```
|
|
103
|
-
import {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<IocProvider>
|
|
111
|
-
<MainProvider>
|
|
112
|
-
<CounterView />
|
|
113
|
-
</MainProvider>
|
|
114
|
-
</IocProvider>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
signal,
|
|
25
|
+
computed,
|
|
26
|
+
effect,
|
|
27
|
+
Signal,
|
|
28
|
+
ReadonlySignal,
|
|
29
|
+
} from '@wirestate/react-signals';
|
|
117
30
|
```
|
|
118
31
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Directly use services and rely on mobx reactivity.
|
|
122
|
-
Or use specialized hooks for communication without direct references.
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
import { useInjection, useCommandCaller, useEventEmitter } from '@wirestate/react';
|
|
126
|
-
import { observer } from '@wirestate/react-mobx';
|
|
127
|
-
import { CounterService } from './CounterService';
|
|
128
|
-
|
|
129
|
-
export const CounterView = observer(() => {
|
|
130
|
-
const service = useInjection(CounterService);
|
|
131
|
-
const call = useCommandCaller();
|
|
132
|
-
const emit = useEventEmitter();
|
|
32
|
+
Example service:
|
|
133
33
|
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
```
|
|
145
|
-
|
|
146
|
-
## Quick Start with signals
|
|
147
|
-
|
|
148
|
-
### 1. Define a Service
|
|
149
|
-
|
|
150
|
-
Services use `signal` and `computed` for state management.
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
import { Injectable, Inject, WireScope, OnEvent, OnCommand, OnQuery } from '@wirestate/core';
|
|
34
|
+
```ts
|
|
35
|
+
import { Injectable, Inject, WireScope } from '@wirestate/core';
|
|
154
36
|
import { signal, computed, Signal, ReadonlySignal } from '@wirestate/react-signals';
|
|
155
37
|
|
|
156
38
|
@Injectable()
|
|
@@ -158,162 +40,14 @@ export class CounterService {
|
|
|
158
40
|
public readonly count: Signal<number> = signal(0);
|
|
159
41
|
public readonly isEven: ReadonlySignal<boolean> = computed(() => this.count.value % 2 === 0);
|
|
160
42
|
|
|
161
|
-
public constructor(
|
|
162
|
-
@Inject(WireScope)
|
|
163
|
-
private scope: WireScope
|
|
164
|
-
) {}
|
|
165
|
-
|
|
166
|
-
public increment(amount: number = 1): void {
|
|
167
|
-
this.count.value += amount;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
public reset(): void {
|
|
171
|
-
this.count.value = 0;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
@OnCommand('INCREMENT')
|
|
175
|
-
public onIncrementCommand(amount: number = 1): void {
|
|
176
|
-
this.increment(amount);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
@OnQuery('GET_TOTAL')
|
|
180
|
-
public onGetTotal(): number {
|
|
181
|
-
return this.count.value;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@OnEvent('RESET')
|
|
185
|
-
public onResetEvent(): void {
|
|
186
|
-
this.reset();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### 2. Configure the Provider
|
|
192
|
-
|
|
193
|
-
The provider configuration remains the same regardless of the reactivity implementation.
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
import { IocProvider, createInjectablesProvider } from '@wirestate/react';
|
|
197
|
-
import { CounterService } from './CounterService';
|
|
198
|
-
|
|
199
|
-
const MainProvider = createInjectablesProvider([CounterService]);
|
|
200
|
-
|
|
201
|
-
export function Application() {
|
|
202
|
-
return (
|
|
203
|
-
<IocProvider>
|
|
204
|
-
<MainProvider>
|
|
205
|
-
<CounterView />
|
|
206
|
-
</MainProvider>
|
|
207
|
-
</IocProvider>
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### 3. Consume in Components
|
|
213
|
-
|
|
214
|
-
Access signals directly in components. Reactivity is handled by the signals transform or manual subscription.
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
import { useInjection, useCommandCaller, useEventEmitter } from '@wirestate/react';
|
|
218
|
-
import { CounterService } from './CounterService';
|
|
219
|
-
|
|
220
|
-
export function CounterView() {
|
|
221
|
-
const service = useInjection(CounterService);
|
|
222
|
-
const call = useCommandCaller();
|
|
223
|
-
const emit = useEventEmitter();
|
|
224
|
-
|
|
225
|
-
return (
|
|
226
|
-
<div>
|
|
227
|
-
<p>Count: {service.count}</p>
|
|
228
|
-
<p>Even: {service.isEven.value ? 'Yes' : 'No'}</p>
|
|
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
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## Advanced Usage
|
|
239
|
-
|
|
240
|
-
### Seeding Shared Initial State
|
|
241
|
-
|
|
242
|
-
`wirestate` supports providing initial data (seeds) to services during activation.
|
|
243
|
-
|
|
244
|
-
```tsx
|
|
245
|
-
const MainProvider = createInjectablesProvider([CounterService]);
|
|
246
|
-
|
|
247
|
-
<IocProvider>
|
|
248
|
-
<MainProvider seed={{ initialCount: 100 }}>
|
|
249
|
-
<CounterView />
|
|
250
|
-
</MainProvider>
|
|
251
|
-
</IocProvider>
|
|
252
|
-
```
|
|
43
|
+
public constructor(@Inject(WireScope) private scope: WireScope) {}
|
|
253
44
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
```typescript
|
|
257
|
-
import { Injectable, Inject, SEED } from 'wirestate';
|
|
258
|
-
|
|
259
|
-
@Injectable()
|
|
260
|
-
export class CounterService {
|
|
261
|
-
// ...
|
|
262
|
-
public constructor(
|
|
263
|
-
@Inject(SEED)
|
|
264
|
-
initialState: { initialCount: number }
|
|
265
|
-
) {
|
|
266
|
-
this.count = seed.initialCount;
|
|
45
|
+
public increment(): void {
|
|
46
|
+
this.count.value++;
|
|
267
47
|
}
|
|
268
48
|
}
|
|
269
49
|
```
|
|
270
50
|
|
|
271
|
-
### Seeding Bound Initial State
|
|
272
|
-
|
|
273
|
-
`wirestate` supports providing initial data (seeds) to services during activation.
|
|
274
|
-
|
|
275
|
-
```tsx
|
|
276
|
-
const MainProvider = createInjectablesProvider([CounterService]);
|
|
277
|
-
|
|
278
|
-
<IocProvider>
|
|
279
|
-
<MainProvider seeds={[[CounterService, { count: 10 }]]}>
|
|
280
|
-
<CounterView />
|
|
281
|
-
</MainProvider>
|
|
282
|
-
</IocProvider>
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
In the service:
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
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
|
-
```
|
|
298
|
-
|
|
299
|
-
### Service Lifecycle
|
|
300
|
-
|
|
301
|
-
Use decorators to handle initialization and cleanup.
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
import { OnActivated, OnDeactivation } from 'wirestate';
|
|
305
|
-
|
|
306
|
-
@OnActivated()
|
|
307
|
-
public onActivated(): void {
|
|
308
|
-
// Start polling, fetch initial data, etc.
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
@OnDeactivation()
|
|
312
|
-
public onDeactivation(): void {
|
|
313
|
-
// Cleanup subscriptions
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
51
|
## License
|
|
318
52
|
|
|
319
53
|
MIT
|