@pravosleva/reactive-engine 0.2.7-beta → 0.2.8-beta
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 +3 -135
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,138 +1,6 @@
|
|
|
1
1
|
# 🚀 ReactiveEngine Core Framework
|
|
2
2
|
A lightweight, type-safe reactive engine built with TypeScript, featuring Dependency Injection and seamless React integration.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
yarn add @pravosleva/reactive-engine
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## 📦 Core Components
|
|
11
|
-
### 1. ReactiveEngine
|
|
12
|
-
The central hub of the system. It manages state, effects, DI, and the bridge to React.
|
|
13
|
-
|
|
14
|
-
- signal<T>(value, options) — Creates an atomic state unit.
|
|
15
|
-
- computed<T>(fn) — Derives data with automatic caching.
|
|
16
|
-
- reactive<T>(obj) — Provides deep reactivity for objects and arrays via Proxy.
|
|
17
|
-
- resource<T>(fetcher, source?) — Handles async requests with auto-abort (Race Condition protection).
|
|
18
|
-
- inject<T>(Class) — Retrieves or instantiates a singleton service.
|
|
19
|
-
- use(signal) — A React hook to subscribe to reactive updates.
|
|
20
|
-
- untrack(fn) — Executes a function without creating reactive dependencies.
|
|
21
|
-
|
|
22
|
-
### 2. BaseREService
|
|
23
|
-
An abstract class for encapsulating business logic.
|
|
24
|
-
- Rule: Always initialize signals and resources directly in class fields to ensure correct DI instantiation and type inference.
|
|
25
|
-
|
|
26
|
-
## 🛠 Quick Start
|
|
27
|
-
### Step 1: Initialization (Entry Point)
|
|
28
|
-
Create and export a single instance of the engine to be used throughout your app.
|
|
29
|
-
|
|
30
|
-
Your instance in your local `~/utils/engine.ts`:
|
|
31
|
-
```ts
|
|
32
|
-
import { useState, useEffect } from 'react';
|
|
33
|
-
import { ReactiveEngine } from '@pravosleva/reactive-engine';
|
|
34
|
-
|
|
35
|
-
export const engine = new ReactiveEngine();
|
|
36
|
-
// Bridge the engine with React hooks
|
|
37
|
-
engine.setReactAdapters(useState, useEffect);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Step 2: Define Business Logic (Service)
|
|
41
|
-
Inherit from BaseREService to create a reactive store.
|
|
42
|
-
```ts
|
|
43
|
-
import { BaseREService, Signal, Computed } from '@pravosleva/reactive-engine';
|
|
44
|
-
|
|
45
|
-
export class CounterService extends BaseREService {
|
|
46
|
-
// Initialize signals with runtime validation
|
|
47
|
-
public count = this.engine.signal(0, {
|
|
48
|
-
name: 'counter',
|
|
49
|
-
validate: (v) => v >= 0 || "Value cannot be negative"
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Derived state (auto-updates when count changes)
|
|
53
|
-
public double = this.engine.computed(() => this.count.value * 2);
|
|
54
|
-
|
|
55
|
-
increment = () => this.count.value++;
|
|
56
|
-
decrement = () => this.count.value--;
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Step 3: Use in React Components
|
|
61
|
-
Connect your logic to the UI with minimal boilerplate.
|
|
62
|
-
|
|
63
|
-
```ts
|
|
64
|
-
import { engine } from '~/utils/engine';
|
|
65
|
-
import { CounterService } from './CounterService';
|
|
66
|
-
|
|
67
|
-
export const Counter = () => {
|
|
68
|
-
// Get the service singleton via DI
|
|
69
|
-
const store = engine.inject(CounterService);
|
|
70
|
-
|
|
71
|
-
// Subscribe to reactive updates
|
|
72
|
-
const count = engine.use(store.count);
|
|
73
|
-
const double = engine.use(store.double);
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<div>
|
|
77
|
-
<h1>Count: {count} (Double: {double})</h1>
|
|
78
|
-
<button onClick={store.increment}>+</button>
|
|
79
|
-
<button onClick={store.decrement}>-</button>
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
};
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## ⚡️ Advanced Features
|
|
86
|
-
Async Resources
|
|
87
|
-
The resource method automatically re-fetches data whenever its source dependency changes.
|
|
88
|
-
```ts
|
|
89
|
-
this.userProfile = this.engine.resource(
|
|
90
|
-
async (id, signal) => {
|
|
91
|
-
const res = await fetch(`https://example.com/{id}`, { signal });
|
|
92
|
-
if (!res.ok) throw new Error('Not found');
|
|
93
|
-
return res.json();
|
|
94
|
-
},
|
|
95
|
-
this.userId // Re-runs when this signal changes
|
|
96
|
-
);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Debugging & Logging
|
|
100
|
-
Enable global logging to track every state change in your application:
|
|
101
|
-
```ts
|
|
102
|
-
engine.onSignalChange = (name, next, prev) => {
|
|
103
|
-
console.log(`%c[${name}]`, 'color: #2196F3; font-weight: bold;', prev, '→', next);
|
|
104
|
-
};
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## ⚠️ Best Practices
|
|
108
|
-
- Always Initialize in Fields: Define reactive properties as class fields. Avoid initializing them inside methods to prevent undefined errors during rendering.
|
|
109
|
-
- Naming Convention: Provide clear names for signals (e.g., this.engine.signal(0, 'my_signal_name')) for better debugging logs.
|
|
110
|
-
- Batching: Use engine.batch(() => { ... }) when updating multiple signals to prevent unnecessary re-renders.
|
|
111
|
-
- Untrack: Use engine.untrack(() => signal.value) inside effects if you need to read a value without subscribing to it.
|
|
112
|
-
|
|
113
|
-
## Possible project structure (for example)
|
|
114
|
-
```
|
|
115
|
-
src/
|
|
116
|
-
├── utils/
|
|
117
|
-
│ └── engine.ts # ReactiveEngine instance
|
|
118
|
-
│
|
|
119
|
-
├── services/ # Business-logic (Store)
|
|
120
|
-
│ ├── index.ts # DI exports (?)
|
|
121
|
-
│ ├── User/
|
|
122
|
-
│ │ ├── UserService.ts # User logic
|
|
123
|
-
│ │ └── types.ts # DTO & data interfaces
|
|
124
|
-
│ └── Counter/
|
|
125
|
-
│ └── CounterService.ts
|
|
126
|
-
│
|
|
127
|
-
├── components/ # UI-layer (React)
|
|
128
|
-
│ ├── Shared/ # Common components
|
|
129
|
-
│ └── Features/ # Components with logic
|
|
130
|
-
│ └── UserProfile/
|
|
131
|
-
│ ├── UserProfile.tsx # Usage of engine.use(store.user)
|
|
132
|
-
│ └── styles.module.css
|
|
133
|
-
│
|
|
134
|
-
├── hooks/ # Global React-hooks
|
|
135
|
-
│ └── useStore.ts # Helpers like useUserStore()
|
|
136
|
-
│
|
|
137
|
-
└── main.tsx # Entry point (settings like engine.setReactAdapters)
|
|
138
|
-
```
|
|
4
|
+
## Instruction
|
|
5
|
+
- 🇬🇧 [In English](https://github.com/garage-13/reactive-engine/blob/main/README_EN.md)
|
|
6
|
+
- 🇷🇺 [In Russian](https://github.com/garage-13/reactive-engine/blob/main/README_RU.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pravosleva/reactive-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8-beta",
|
|
4
4
|
"description": "JS Reactive Engine",
|
|
5
5
|
"author": "Den Pol <selection4test@gmail.com> (https://pravosleva.pro)",
|
|
6
6
|
"homepage": "https://github.com/garage-13/reactive-engine",
|