@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +114 -234
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.6.3
2
+
3
+ - Update readme files for each module
4
+
1
5
  ## 0.6.2
2
6
 
3
7
  - Corrected build system, react package structure
package/README.md CHANGED
@@ -1,319 +1,199 @@
1
- # <a href='https://www.npmjs.com/package/wirestate'> ⚡ wirestate </a>
1
+ # @wirestate/react
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/wirestate.svg?style=flat-square)](https://www.npmjs.com/package/wirestate)
4
- [![language-ts](https://img.shields.io/badge/language-typescript-blue.svg?style=flat)](https://github.com/Neloreck/wirestate/search?l=typescript)
3
+ [![npm](https://img.shields.io/npm/v/@wirestate/react.svg?style=flat-square)](https://www.npmjs.com/package/@wirestate/react)
5
4
  [![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Neloreck/wirestate/blob/master/LICENSE)
6
5
 
7
- `wirestate` is a reactivity-independent state management framework for React. It integrates **InversifyJS** for robust Dependency Injection,
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 --save @wirestate/core reflect-metadata
11
+ npm install @wirestate/core @wirestate/react reflect-metadata
33
12
  ```
34
13
 
35
- ### For react-mobx
36
-
37
- ```bash
38
- npm install --save @wirestate/react @wirestate/react-mobx mobx mobx-react-lite
39
- ```
14
+ ## Providers
40
15
 
41
- ### For signals
16
+ ### `IocProvider`
42
17
 
43
- ```bash
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
- ## Quick Start with mobx
20
+ ```tsx
21
+ import { IocProvider } from '@wirestate/react';
49
22
 
50
- ### 1. Define a Service
23
+ export function App() {
24
+ return (
25
+ <IocProvider>
26
+ <SomeComponent />
27
+ </IocProvider>
28
+ );
29
+ }
30
+ ```
51
31
 
52
- Services are standard classes decorated with `@Injectable`. Use `WireScope` to interact with the framework.
32
+ ### `createInjectablesProvider`
53
33
 
54
- ```typescript
55
- import { Injectable, Inject, WireScope, OnEvent, OnCommand, OnQuery } from '@wirestate/core';
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
- @Injectable()
59
- export class CounterService {
60
- @Observable()
61
- public count: number = 0;
37
+ ```tsx
38
+ import { createInjectablesProvider } from '@wirestate/react';
39
+ import { CounterService, LoggerService } from './services';
62
40
 
63
- public constructor(
64
- @Inject(WireScope)
65
- private scope: WireScope
66
- ) {
67
- makeObservable(this);
68
- }
41
+ const InjectionProvider = createInjectablesProvider([CounterService, LoggerService]);
69
42
 
70
- @Action()
71
- public increment(amount: number = 1): void {
72
- this.count += amount;
73
- }
43
+ export function CounterPage() {
44
+ return (
45
+ <InjectionProvider>
46
+ <CounterView />
47
+ </InjectionProvider>
48
+ );
49
+ }
50
+ ```
74
51
 
75
- @Action()
76
- public reset(): void {
77
- this.count = 0;
78
- }
52
+ **Props:**
79
53
 
80
- @OnCommand('INCREMENT')
81
- public onIncrementCommand(amount: number = 1): void {
82
- this.increment(amount);
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
- @OnQuery('GET_TOTAL')
86
- public onGetTotal(): number {
87
- return this.count;
88
- }
59
+ Both `seed` and `seeds` are applied on first render only.
89
60
 
90
- @OnEvent('RESET')
91
- public onResetEvent(): void {
92
- this.reset();
93
- }
94
- }
95
- ```
61
+ ## Injection hooks
96
62
 
97
- ### 2. Configure the Provider
63
+ ### `useInjection(token)`
98
64
 
99
- Bind services at any level of the component tree.
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 { IocProvider, createInjectablesProvider } from '@wirestate/react';
104
- import { CounterService } from './CounterService';
68
+ import { useInjection } from '@wirestate/react';
69
+ import { CounterService } from './services';
105
70
 
106
- const MainProvider = createInjectablesProvider([CounterService]);
107
-
108
- export function Application() {
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
- ### 3. Consume in Components
77
+ ### `useOptionalInjection(token)`
120
78
 
121
- Directly use services and rely on mobx reactivity.
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
- ```tsx
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
- export const CounterView = observer(() => {
130
- const service = useInjection(CounterService);
131
- const call = useCommandCaller();
132
- const emit = useEventEmitter();
83
+ ### `useEventEmitter()`
133
84
 
134
- return (
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
- ## Quick Start with signals
87
+ ```tsx
88
+ const emit = useEventEmitter();
147
89
 
148
- ### 1. Define a Service
90
+ emit('RESET');
91
+ emit('ADD', { amount: 5 });
92
+ ```
149
93
 
150
- Services use `signal` and `computed` for state management.
94
+ ### `useEvent(type, handler)`
151
95
 
152
- ```typescript
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
- @Injectable()
157
- export class CounterService {
158
- public readonly count: Signal<number> = signal(0);
159
- public readonly isEven: ReadonlySignal<boolean> = computed(() => this.count.value % 2 === 0);
98
+ ```tsx
99
+ useEvent('RESET', (event) => {
100
+ console.log(event.type, event.payload);
101
+ });
102
+ ```
160
103
 
161
- public constructor(
162
- @Inject(WireScope)
163
- private scope: WireScope
164
- ) {}
104
+ ### `useEvents(types, handler)`
165
105
 
166
- public increment(amount: number = 1): void {
167
- this.count.value += amount;
168
- }
106
+ Subscribes to multiple event types with a single handler.
169
107
 
170
- public reset(): void {
171
- this.count.value = 0;
172
- }
108
+ ```tsx
109
+ useEvents(['RESET', 'CLEAR'], (event) => {
110
+ console.log(event.type, event.payload);
111
+ });
112
+ ```
173
113
 
174
- @OnCommand('INCREMENT')
175
- public onIncrementCommand(amount: number = 1): void {
176
- this.increment(amount);
177
- }
114
+ ### `useEventsHandler(handler)`
178
115
 
179
- @OnQuery('GET_TOTAL')
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
- @OnEvent('RESET')
185
- public onResetEvent(): void {
186
- this.reset();
187
- }
188
- }
118
+ ```tsx
119
+ useEventsHandler((event) => {
120
+ logger.log(event.type, event.payload);
121
+ });
189
122
  ```
190
123
 
191
- ### 2. Configure the Provider
124
+ ## Command hooks
192
125
 
193
- The provider configuration remains the same regardless of the reactivity implementation.
126
+ ### `useCommandCaller()`
194
127
 
195
- ```tsx
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
- const MainProvider = createInjectablesProvider([CounterService]);
130
+ ```tsx
131
+ const call = useCommandCaller();
200
132
 
201
- export function Application() {
202
- return (
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
- ### 3. Consume in Components
138
+ ### `useOptionalCommandCaller()`
213
139
 
214
- Access signals directly in components. Reactivity is handled by the signals transform or manual subscription.
140
+ Same as `useCommandCaller` but returns `null` if no handler is registered.
215
141
 
216
- ```tsx
217
- import { useInjection, useCommandCaller, useEventEmitter } from '@wirestate/react';
218
- import { CounterService } from './CounterService';
142
+ ### `useCommandHandler(type, handler)`
219
143
 
220
- export function CounterView() {
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
- 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
- }
146
+ ```tsx
147
+ useCommandHandler('SCROLL_TOP', () => {
148
+ window.scrollTo(0, 0);
149
+ });
236
150
  ```
237
151
 
238
- ## Advanced Usage
152
+ ## Query hooks
239
153
 
240
- ### Seeding Shared Initial State
154
+ ### `useQueryCaller()`
241
155
 
242
- `wirestate` supports providing initial data (seeds) to services during activation.
156
+ Returns an async function that calls a query handler and resolves its return value.
243
157
 
244
158
  ```tsx
245
- const MainProvider = createInjectablesProvider([CounterService]);
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
- In the service:
163
+ ### `useSyncQueryCaller()`
255
164
 
256
- ```typescript
257
- import { Injectable, Inject, SEED } from 'wirestate';
165
+ Same as `useQueryCaller` but calls a synchronous handler.
258
166
 
259
- @Injectable()
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
- ### Seeding Bound Initial State
169
+ Return `null` when no handler is registered instead of throwing.
272
170
 
273
- `wirestate` supports providing initial data (seeds) to services during activation.
171
+ ### `useQueryHandler(type, handler)`
274
172
 
275
- ```tsx
276
- const MainProvider = createInjectablesProvider([CounterService]);
173
+ Registers a query handler from a component.
277
174
 
278
- <IocProvider>
279
- <MainProvider seeds={[[CounterService, { count: 10 }]]}>
280
- <CounterView />
281
- </MainProvider>
282
- </IocProvider>
175
+ ```tsx
176
+ useQueryHandler('GET_SCROLL_POS', () => window.scrollY);
283
177
  ```
284
178
 
285
- In the service:
179
+ ## Container hooks
286
180
 
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
- ```
181
+ ### `useContainer()`
298
182
 
299
- ### Service Lifecycle
183
+ Returns the nearest `Container` instance. Useful for advanced manual resolution.
300
184
 
301
- Use decorators to handle initialization and cleanup.
185
+ ### `useContainerRevision()`
302
186
 
303
- ```typescript
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
- @OnActivated()
307
- public onActivated(): void {
308
- // Start polling, fetch initial data, etc.
309
- }
189
+ ## Test utilities
310
190
 
311
- @OnDeactivation()
312
- public onDeactivation(): void {
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirestate/react",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "React alias and adapters for wirestate",
5
5
  "sideEffects": false,
6
6
  "author": "Syrotenko Igor",