@fozy-labs/rx-toolkit 0.5.3-rc.1 → 0.5.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/LICENSE +21 -21
- package/README.md +143 -137
- package/dist/common/devtools/combineDevtools.js +3 -3
- package/dist/common/devtools/index.d.ts +3 -3
- package/dist/common/devtools/index.js +3 -3
- package/dist/common/devtools/reduxDevtools.d.ts +1 -1
- package/dist/common/devtools/reduxDevtools.js +17 -17
- package/dist/common/devtools/types.d.ts +0 -6
- package/dist/common/options/DefaultOptions.d.ts +1 -1
- package/dist/common/options/SharedOptions.d.ts +3 -2
- package/dist/common/options/SharedOptions.js +6 -0
- package/dist/common/options/index.d.ts +1 -1
- package/dist/common/options/index.js +1 -1
- package/dist/common/react/index.d.ts +2 -2
- package/dist/common/react/index.js +2 -2
- package/dist/common/react/useConstant.js +1 -1
- package/dist/common/utils/deepEqual.js +1 -1
- package/dist/common/utils/index.d.ts +3 -3
- package/dist/common/utils/index.js +3 -3
- package/dist/common/utils/shallowEqual.js +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/query/SKIP_TOKEN.js +1 -1
- package/dist/query/api/createCommand.d.ts +21 -0
- package/dist/query/api/createCommand.js +20 -0
- package/dist/query/api/createOperation.d.ts +5 -3
- package/dist/query/api/createOperation.js +6 -2
- package/dist/query/api/createResource.d.ts +2 -2
- package/dist/query/api/createResourceDuplicator.d.ts +2 -2
- package/dist/query/core/Command/Command.d.ts +35 -0
- package/dist/query/core/{Opertation/Operation.js → Command/Command.js} +13 -14
- package/dist/query/core/Command/CommandAgent.d.ts +19 -0
- package/dist/query/core/{Opertation/OperationAgent.js → Command/CommandAgent.js} +13 -13
- package/dist/query/core/Command/index.d.ts +2 -0
- package/dist/query/core/Command/index.js +2 -0
- package/dist/query/core/Operation/Operation.d.ts +8 -0
- package/dist/query/core/Operation/Operation.js +4 -0
- package/dist/query/core/Operation/OperationAgent.d.ts +4 -0
- package/dist/query/core/Operation/OperationAgent.js +4 -0
- package/dist/query/core/QueriesCache.d.ts +2 -2
- package/dist/query/core/QueriesCache.js +1 -1
- package/dist/query/core/QueriesLifetimeHooks.d.ts +1 -1
- package/dist/query/core/QueriesLifetimeHooks.js +7 -7
- package/dist/query/core/Resource/Resource.d.ts +16 -16
- package/dist/query/core/Resource/Resource.js +7 -7
- package/dist/query/core/Resource/ResourceAgent.d.ts +2 -2
- package/dist/query/core/Resource/ResourceAgent.js +3 -3
- package/dist/query/core/Resource/ResourceDuplicator.d.ts +17 -17
- package/dist/query/core/Resource/ResourceDuplicator.js +18 -20
- package/dist/query/core/Resource/ResourceDuplicatorAgent.d.ts +6 -6
- package/dist/query/core/Resource/ResourceDuplicatorAgent.js +3 -3
- package/dist/query/core/Resource/ResourceRef.d.ts +2 -2
- package/dist/query/core/Resource/ResourceRef.js +12 -12
- package/dist/query/index.d.ts +11 -8
- package/dist/query/index.js +14 -8
- package/dist/query/lib/IndirectMap.js +4 -4
- package/dist/query/lib/ReactiveCache.d.ts +1 -1
- package/dist/query/react/useCommandAgent.d.ts +24 -0
- package/dist/query/react/useCommandAgent.js +39 -0
- package/dist/query/react/useOperationAgent.d.ts +6 -8
- package/dist/query/react/useOperationAgent.js +6 -23
- package/dist/query/react/useResourceAgent.d.ts +4 -4
- package/dist/query/react/useResourceAgent.js +1 -1
- package/dist/query/react/useResourceRef.d.ts +3 -3
- package/dist/query/react/useResourceRef.js +7 -2
- package/dist/query/types/Command.types.d.ts +154 -0
- package/dist/query/types/Command.types.js +1 -0
- package/dist/query/types/Operation.types.d.ts +13 -154
- package/dist/query/types/Resource.types.d.ts +7 -5
- package/dist/query/types/index.d.ts +4 -3
- package/dist/query/types/index.js +5 -3
- package/dist/query-v2/api/createApi.d.ts +10 -0
- package/dist/query-v2/api/createApi.js +83 -0
- package/dist/query-v2/core/common/CacheEntry.d.ts +29 -0
- package/dist/query-v2/core/common/CacheEntry.js +71 -0
- package/dist/query-v2/core/common/CacheMap.d.ts +38 -0
- package/dist/query-v2/core/common/CacheMap.js +127 -0
- package/dist/query-v2/core/common/LifecycleHooks.d.ts +22 -0
- package/dist/query-v2/core/common/LifecycleHooks.js +104 -0
- package/dist/query-v2/core/common/index.d.ts +3 -0
- package/dist/query-v2/core/common/index.js +3 -0
- package/dist/query-v2/core/index.d.ts +3 -0
- package/dist/query-v2/core/index.js +3 -0
- package/dist/query-v2/core/machines/Machine.d.ts +14 -0
- package/dist/query-v2/core/machines/Machine.js +33 -0
- package/dist/query-v2/core/machines/MachineError.d.ts +11 -0
- package/dist/query-v2/core/machines/MachineError.js +26 -0
- package/dist/query-v2/core/machines/MachineIdle.d.ts +8 -0
- package/dist/query-v2/core/machines/MachineIdle.js +19 -0
- package/dist/query-v2/core/machines/MachinePending.d.ts +12 -0
- package/dist/query-v2/core/machines/MachinePending.js +29 -0
- package/dist/query-v2/core/machines/MachineRefreshing.d.ts +14 -0
- package/dist/query-v2/core/machines/MachineRefreshing.js +46 -0
- package/dist/query-v2/core/machines/MachineSuccess.d.ts +16 -0
- package/dist/query-v2/core/machines/MachineSuccess.js +42 -0
- package/dist/query-v2/core/machines/MachineWithData.d.ts +18 -0
- package/dist/query-v2/core/machines/MachineWithData.js +40 -0
- package/dist/query-v2/core/machines/Patcher.d.ts +20 -0
- package/dist/query-v2/core/machines/Patcher.js +104 -0
- package/dist/query-v2/core/machines/index.d.ts +8 -0
- package/dist/query-v2/core/machines/index.js +8 -0
- package/dist/query-v2/core/resource/ResourceV2.d.ts +120 -0
- package/dist/query-v2/core/resource/ResourceV2.js +464 -0
- package/dist/query-v2/core/resource/ResourceV2Agent.d.ts +26 -0
- package/dist/query-v2/core/resource/ResourceV2Agent.js +132 -0
- package/dist/query-v2/core/resource/index.d.ts +2 -0
- package/dist/query-v2/core/resource/index.js +2 -0
- package/dist/query-v2/index.d.ts +11 -0
- package/dist/query-v2/index.js +17 -0
- package/dist/query-v2/lib/NO_VALUE.d.ts +2 -0
- package/dist/query-v2/lib/NO_VALUE.js +1 -0
- package/dist/query-v2/lib/SKIP_TOKEN.d.ts +2 -0
- package/dist/query-v2/lib/SKIP_TOKEN.js +1 -0
- package/dist/query-v2/lib/index.d.ts +4 -0
- package/dist/query-v2/lib/index.js +3 -0
- package/dist/query-v2/lib/stableStringify.d.ts +8 -0
- package/dist/query-v2/lib/stableStringify.js +23 -0
- package/dist/query-v2/plugins/ReactHooksPlugin.d.ts +25 -0
- package/dist/query-v2/plugins/ReactHooksPlugin.js +19 -0
- package/dist/query-v2/plugins/types.d.ts +1 -0
- package/dist/query-v2/plugins/types.js +1 -0
- package/dist/query-v2/react/__tests__/helpers.d.ts +12 -0
- package/dist/query-v2/react/__tests__/helpers.js +33 -0
- package/dist/query-v2/react/index.d.ts +2 -0
- package/dist/query-v2/react/index.js +2 -0
- package/dist/query-v2/react/useResourceV2Agent.d.ts +12 -0
- package/dist/query-v2/react/useResourceV2Agent.js +36 -0
- package/dist/query-v2/react/useResourceV2Ref.d.ts +12 -0
- package/dist/query-v2/react/useResourceV2Ref.js +57 -0
- package/dist/query-v2/snapshot/Snapshot.d.ts +13 -0
- package/dist/query-v2/snapshot/Snapshot.js +76 -0
- package/dist/query-v2/types/agent.types.d.ts +54 -0
- package/dist/query-v2/types/agent.types.js +1 -0
- package/dist/query-v2/types/api.types.d.ts +22 -0
- package/dist/query-v2/types/api.types.js +1 -0
- package/dist/query-v2/types/cache.types.d.ts +37 -0
- package/dist/query-v2/types/cache.types.js +1 -0
- package/dist/query-v2/types/index.d.ts +9 -0
- package/dist/query-v2/types/index.js +9 -0
- package/dist/query-v2/types/lifecycle.types.d.ts +25 -0
- package/dist/query-v2/types/lifecycle.types.js +1 -0
- package/dist/query-v2/types/machine.types.d.ts +67 -0
- package/dist/query-v2/types/machine.types.js +1 -0
- package/dist/query-v2/types/plugin.types.d.ts +38 -0
- package/dist/query-v2/types/plugin.types.js +1 -0
- package/dist/query-v2/types/resource.types.d.ts +35 -0
- package/dist/query-v2/types/resource.types.js +1 -0
- package/dist/query-v2/types/shared.types.d.ts +20 -0
- package/dist/query-v2/types/shared.types.js +1 -0
- package/dist/query-v2/types/snapshot.types.d.ts +21 -0
- package/dist/query-v2/types/snapshot.types.js +1 -0
- package/dist/signals/base/Batcher.js +9 -5
- package/dist/signals/base/ComputeCache.js +3 -3
- package/dist/signals/base/DependencyTracker.js +1 -1
- package/dist/signals/base/Devtools.d.ts +3 -2
- package/dist/signals/base/Devtools.js +54 -27
- package/dist/signals/base/Indexer.js +1 -1
- package/dist/signals/base/ReadonlySignal.js +1 -1
- package/dist/signals/base/SyncObservable.d.ts +1 -2
- package/dist/signals/base/SyncObservable.js +2 -5
- package/dist/signals/base/index.d.ts +6 -6
- package/dist/signals/base/index.js +6 -6
- package/dist/signals/index.d.ts +5 -4
- package/dist/signals/index.js +5 -4
- package/dist/signals/operators/index.d.ts +1 -1
- package/dist/signals/operators/index.js +1 -1
- package/dist/signals/operators/signalize.d.ts +1 -1
- package/dist/signals/react/index.d.ts +1 -1
- package/dist/signals/react/index.js +1 -1
- package/dist/signals/signals/Computed.d.ts +3 -4
- package/dist/signals/signals/Computed.js +18 -10
- package/dist/signals/signals/Effect.js +2 -1
- package/dist/signals/signals/LocalState.d.ts +44 -0
- package/dist/signals/signals/{LocalSignal.js → LocalState.js} +62 -28
- package/dist/signals/signals/Signal.d.ts +8 -7
- package/dist/signals/signals/Signal.js +4 -1
- package/dist/signals/signals/State.d.ts +4 -5
- package/dist/signals/signals/State.js +23 -9
- package/dist/signals/signals/index.d.ts +5 -5
- package/dist/signals/signals/index.js +5 -6
- package/dist/signals/types/SignalOptions.d.ts +16 -0
- package/dist/signals/types/SignalOptions.js +1 -0
- package/dist/signals/types/index.d.ts +3 -1
- package/dist/signals/types/index.js +3 -1
- package/dist/signals/types/normalizeSignalOptions.d.ts +2 -0
- package/dist/signals/types/normalizeSignalOptions.js +10 -0
- package/dist/signals/types/signals.types.d.ts +6 -2
- package/docs/CHANGELOG.md +111 -32
- package/docs/CONTRIBUTING.md +230 -0
- package/docs/contributing/ai-assisted-development.md +47 -0
- package/docs/contributing/query-v2/README.md +379 -0
- package/docs/{release → contributing/release}/README.md +59 -59
- package/docs/devtools/README.md +228 -228
- package/docs/migrations/0.5.0.md +58 -58
- package/docs/migrations/query-v2.md +171 -0
- package/docs/options/README.md +92 -90
- package/docs/query/README.md +575 -571
- package/docs/query-v2/README.md +280 -0
- package/docs/query-v2/api-reference.md +235 -0
- package/docs/query-v2/optimistic-updates.md +148 -0
- package/docs/query-v2/ssr.md +130 -0
- package/docs/signals/README.md +300 -295
- package/docs/usage/react/README.md +309 -307
- package/package.json +86 -63
- package/dist/query/core/Opertation/Operation.d.ts +0 -35
- package/dist/query/core/Opertation/OperationAgent.d.ts +0 -19
- package/dist/signals/signals/LocalSignal.d.ts +0 -32
package/docs/signals/README.md
CHANGED
|
@@ -1,295 +1,300 @@
|
|
|
1
|
-
# RxSignals
|
|
2
|
-
|
|
3
|
-
RxSignals — это реактивная система управления состоянием, вдохновленная современными фреймворками типа SolidJS и Angular Signals. Она предоставляет эффективные инструменты для создания реактивных приложений.
|
|
4
|
-
|
|
5
|
-
## Основные концепции
|
|
6
|
-
|
|
7
|
-
### Реактивность на основе значений
|
|
8
|
-
|
|
9
|
-
Сигналы (`State`) хранят текущее состояние, а производные сущности (`Computed`, `Effect`) автоматически отслеживают зависимости,
|
|
10
|
-
применяя кеширование на основе *значений*. Это приводит к тому, что в отличие от классического RxJS-подхода,
|
|
11
|
-
где каждое `next()` — это событие, в RxSignals важен именно факт *изменения значения*.
|
|
12
|
-
|
|
13
|
-
### State
|
|
14
|
-
|
|
15
|
-
База для создания реактивных сигналов с изменяемым состоянием.
|
|
16
|
-
|
|
17
|
-
**Пример использования:**
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
21
|
-
|
|
22
|
-
const name = Signal.state('John');
|
|
23
|
-
const age = Signal.state(25);
|
|
24
|
-
|
|
25
|
-
// Чтение значения (с отслеживанием зависимостей)
|
|
26
|
-
console.log(name()); // "John"
|
|
27
|
-
|
|
28
|
-
// Чтение значения без отслеживания
|
|
29
|
-
console.log(name.peek()); // "John"
|
|
30
|
-
|
|
31
|
-
// Запись нового значения
|
|
32
|
-
name.set('Jane');
|
|
33
|
-
|
|
34
|
-
// Подписка на изменения через RxJS Observable
|
|
35
|
-
const subscription = name.obs.subscribe(newName => {
|
|
36
|
-
console.log(`Name changed to: ${newName}`);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Отписка
|
|
40
|
-
subscription.unsubscribe();
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
**API Signal:**
|
|
44
|
-
- `()`|`get()` — получить значение и зарегистрировать зависимость (для использования внутри Computed/Effect)
|
|
45
|
-
- `peek()` — получить значение без регистрации зависимости
|
|
46
|
-
- `set(value)` — установить новое значение
|
|
47
|
-
- `obs` — RxJS Observable для подписки на изменения
|
|
48
|
-
|
|
49
|
-
### Computed
|
|
50
|
-
|
|
51
|
-
Создает вычисляемое значение, которое автоматически обновляется при изменении зависимостей.
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
55
|
-
|
|
56
|
-
const firstName = Signal.state('John');
|
|
57
|
-
const lastName = Signal.state('Doe');
|
|
58
|
-
|
|
59
|
-
const fullName = Signal.compute(() => `${firstName()} ${lastName()}`);
|
|
60
|
-
|
|
61
|
-
console.log(fullName()); // "John Doe"
|
|
62
|
-
|
|
63
|
-
firstName.set('Jane');
|
|
64
|
-
console.log(fullName()); // "Jane Doe"
|
|
65
|
-
|
|
66
|
-
// Подписка на изменения
|
|
67
|
-
fullName.obs.subscribe(name => console.log(name));
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**API Computed:**
|
|
71
|
-
- `()`|`get()` — получить вычисленное значение с регистрацией зависимости
|
|
72
|
-
- `peek()` — получить значение без регистрации зависимости
|
|
73
|
-
- `obs` — RxJS Observable для подписки на изменения
|
|
74
|
-
|
|
75
|
-
Также на данный момент Computed
|
|
76
|
-
|
|
77
|
-
### Effect
|
|
78
|
-
|
|
79
|
-
Создает побочный эффект, который автоматически выполняется при изменении используемых сигналов.
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
83
|
-
|
|
84
|
-
const count = Signal.state(0);
|
|
85
|
-
const message = Signal.state('Hello');
|
|
86
|
-
|
|
87
|
-
const effect = Signal.effect(() => {
|
|
88
|
-
// Выведет: "Hello: 0" при инициализации
|
|
89
|
-
console.log(`${message()}: ${count()}`);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
count.set(1); // Выведет: "Hello: 1"
|
|
93
|
-
message.set('Hi'); // Выведет: "Hi: 1"
|
|
94
|
-
|
|
95
|
-
// Остановка эффекта
|
|
96
|
-
effect.unsubscribe();
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Cleanup функция (teardown):**
|
|
100
|
-
|
|
101
|
-
Effect поддерживает возврат функции очистки, которая вызывается перед следующим выполнением или при отписке:
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
const effect = Signal.effect(() => {
|
|
105
|
-
count(); // Создаем подписку на count (тк не работает при асинхронных операциях)
|
|
106
|
-
const timer = setInterval(() => count(), 1000);
|
|
107
|
-
|
|
108
|
-
// Cleanup - вызывается перед повторным выполнением эффекта
|
|
109
|
-
return () => {
|
|
110
|
-
clearInterval(timer);
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Функциональный vs классовый стиль
|
|
116
|
-
|
|
117
|
-
RxSignals поддерживает как функциональный, так и классовый стили создания сигналов, позволяя выбрать подход в зависимости от предпочтений и архитектуры приложения.
|
|
118
|
-
#### Функциональный стиль (рекомендуемый)
|
|
119
|
-
|
|
120
|
-
Используйте статические методы `Signal.state`,`Signal.compute` и `Signal.effect` для создания сигналов.
|
|
121
|
-
Этот стиль лаконичен, похож на SolidJS и подходит для большинства случаев:
|
|
122
|
-
|
|
123
|
-
```tszz
|
|
124
|
-
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
125
|
-
|
|
126
|
-
const count = Signal.state(0);
|
|
127
|
-
const doubled = Signal.compute(() => count() * 2);
|
|
128
|
-
const logEffect = Signal.effect(() => console.log(doubled()));
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
#### Классовый стиль
|
|
132
|
-
|
|
133
|
-
Создавайте экземпляры классов Signal, Computed и Effect напрямую.
|
|
134
|
-
Этот стиль более явный, похож на RxJs и полезен для наследования или сложной логики,
|
|
135
|
-
учтите, что вызов `()` недоступен и нужно использовать `get()`:
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import { State, Computed, Effect } from '@fozy-labs/rx-toolkit';
|
|
139
|
-
|
|
140
|
-
const count = new State(0);
|
|
141
|
-
const doubled = new Computed(() => count.get() * 2);
|
|
142
|
-
const logEffect = new Effect(() => console.log(doubled.get()));
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### ReadonlySignal
|
|
146
|
-
|
|
147
|
-
Базовый класс для сигналов только для чтения. Используется внутри `signalize` и для создания кастомных сигналов.
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
import { ReadonlySignal } from '@fozy-labs/rx-toolkit';
|
|
151
|
-
|
|
152
|
-
const customSignal = new ReadonlySignal((subscriber) => {
|
|
153
|
-
// Логика подписки
|
|
154
|
-
subscriber.next(initialValue);
|
|
155
|
-
return () => {
|
|
156
|
-
// Cleanup
|
|
157
|
-
};
|
|
158
|
-
});
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
###
|
|
162
|
-
|
|
163
|
-
Сигнал, который автоматически синхронизируется с `localStorage`.
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
import { z } from 'zod/v4';
|
|
167
|
-
import {
|
|
168
|
-
|
|
169
|
-
enum FILTER {
|
|
170
|
-
ALL = 'all',
|
|
171
|
-
CHANNELS = 'channels',
|
|
172
|
-
CHATS = 'chats',
|
|
173
|
-
MEETINGS = 'meetings',
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const selectedFilter$ =
|
|
177
|
-
key: 'memberships-list-selected-filter',
|
|
178
|
-
defaultValue: FILTER.ALL,
|
|
179
|
-
zodSchema: z.nativeEnum(FILTER), // Опционально: валидация через Zod
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Использование
|
|
183
|
-
console.log(selectedFilter
|
|
184
|
-
selectedFilter$.set(FILTER.CHANNELS); // Сохраняется в localStorage
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
- `
|
|
193
|
-
- `
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
tenClicks
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
1
|
+
# RxSignals
|
|
2
|
+
|
|
3
|
+
RxSignals — это реактивная система управления состоянием, вдохновленная современными фреймворками типа SolidJS и Angular Signals. Она предоставляет эффективные инструменты для создания реактивных приложений.
|
|
4
|
+
|
|
5
|
+
## Основные концепции
|
|
6
|
+
|
|
7
|
+
### Реактивность на основе значений
|
|
8
|
+
|
|
9
|
+
Сигналы (`State`) хранят текущее состояние, а производные сущности (`Computed`, `Effect`) автоматически отслеживают зависимости,
|
|
10
|
+
применяя кеширование на основе *значений*. Это приводит к тому, что в отличие от классического RxJS-подхода,
|
|
11
|
+
где каждое `next()` — это событие, в RxSignals важен именно факт *изменения значения*.
|
|
12
|
+
|
|
13
|
+
### State
|
|
14
|
+
|
|
15
|
+
База для создания реактивных сигналов с изменяемым состоянием.
|
|
16
|
+
|
|
17
|
+
**Пример использования:**
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
21
|
+
|
|
22
|
+
const name = Signal.state('John');
|
|
23
|
+
const age = Signal.state(25);
|
|
24
|
+
|
|
25
|
+
// Чтение значения (с отслеживанием зависимостей)
|
|
26
|
+
console.log(name()); // "John"
|
|
27
|
+
|
|
28
|
+
// Чтение значения без отслеживания
|
|
29
|
+
console.log(name.peek()); // "John"
|
|
30
|
+
|
|
31
|
+
// Запись нового значения
|
|
32
|
+
name.set('Jane');
|
|
33
|
+
|
|
34
|
+
// Подписка на изменения через RxJS Observable
|
|
35
|
+
const subscription = name.obs.subscribe(newName => {
|
|
36
|
+
console.log(`Name changed to: ${newName}`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Отписка
|
|
40
|
+
subscription.unsubscribe();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**API Signal:**
|
|
44
|
+
- `()`|`get()` — получить значение и зарегистрировать зависимость (для использования внутри Computed/Effect)
|
|
45
|
+
- `peek()` — получить значение без регистрации зависимости
|
|
46
|
+
- `set(value)` — установить новое значение
|
|
47
|
+
- `obs` — RxJS Observable для подписки на изменения
|
|
48
|
+
|
|
49
|
+
### Computed
|
|
50
|
+
|
|
51
|
+
Создает вычисляемое значение, которое автоматически обновляется при изменении зависимостей.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
55
|
+
|
|
56
|
+
const firstName = Signal.state('John');
|
|
57
|
+
const lastName = Signal.state('Doe');
|
|
58
|
+
|
|
59
|
+
const fullName = Signal.compute(() => `${firstName()} ${lastName()}`);
|
|
60
|
+
|
|
61
|
+
console.log(fullName()); // "John Doe"
|
|
62
|
+
|
|
63
|
+
firstName.set('Jane');
|
|
64
|
+
console.log(fullName()); // "Jane Doe"
|
|
65
|
+
|
|
66
|
+
// Подписка на изменения
|
|
67
|
+
fullName.obs.subscribe(name => console.log(name));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**API Computed:**
|
|
71
|
+
- `()`|`get()` — получить вычисленное значение с регистрацией зависимости
|
|
72
|
+
- `peek()` — получить значение без регистрации зависимости
|
|
73
|
+
- `obs` — RxJS Observable для подписки на изменения
|
|
74
|
+
|
|
75
|
+
Также на данный момент Computed
|
|
76
|
+
|
|
77
|
+
### Effect
|
|
78
|
+
|
|
79
|
+
Создает побочный эффект, который автоматически выполняется при изменении используемых сигналов.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
83
|
+
|
|
84
|
+
const count = Signal.state(0);
|
|
85
|
+
const message = Signal.state('Hello');
|
|
86
|
+
|
|
87
|
+
const effect = Signal.effect(() => {
|
|
88
|
+
// Выведет: "Hello: 0" при инициализации
|
|
89
|
+
console.log(`${message()}: ${count()}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
count.set(1); // Выведет: "Hello: 1"
|
|
93
|
+
message.set('Hi'); // Выведет: "Hi: 1"
|
|
94
|
+
|
|
95
|
+
// Остановка эффекта
|
|
96
|
+
effect.unsubscribe();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Cleanup функция (teardown):**
|
|
100
|
+
|
|
101
|
+
Effect поддерживает возврат функции очистки, которая вызывается перед следующим выполнением или при отписке:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const effect = Signal.effect(() => {
|
|
105
|
+
count(); // Создаем подписку на count (тк не работает при асинхронных операциях)
|
|
106
|
+
const timer = setInterval(() => count(), 1000);
|
|
107
|
+
|
|
108
|
+
// Cleanup - вызывается перед повторным выполнением эффекта
|
|
109
|
+
return () => {
|
|
110
|
+
clearInterval(timer);
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Функциональный vs классовый стиль
|
|
116
|
+
|
|
117
|
+
RxSignals поддерживает как функциональный, так и классовый стили создания сигналов, позволяя выбрать подход в зависимости от предпочтений и архитектуры приложения.
|
|
118
|
+
#### Функциональный стиль (рекомендуемый)
|
|
119
|
+
|
|
120
|
+
Используйте статические методы `Signal.state`,`Signal.compute` и `Signal.effect` для создания сигналов.
|
|
121
|
+
Этот стиль лаконичен, похож на SolidJS и подходит для большинства случаев:
|
|
122
|
+
|
|
123
|
+
```tszz
|
|
124
|
+
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
125
|
+
|
|
126
|
+
const count = Signal.state(0);
|
|
127
|
+
const doubled = Signal.compute(() => count() * 2);
|
|
128
|
+
const logEffect = Signal.effect(() => console.log(doubled()));
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
#### Классовый стиль
|
|
132
|
+
|
|
133
|
+
Создавайте экземпляры классов Signal, Computed и Effect напрямую.
|
|
134
|
+
Этот стиль более явный, похож на RxJs и полезен для наследования или сложной логики,
|
|
135
|
+
учтите, что вызов `()` недоступен и нужно использовать `get()`:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { State, Computed, Effect } from '@fozy-labs/rx-toolkit';
|
|
139
|
+
|
|
140
|
+
const count = new State(0);
|
|
141
|
+
const doubled = new Computed(() => count.get() * 2);
|
|
142
|
+
const logEffect = new Effect(() => console.log(doubled.get()));
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### ReadonlySignal
|
|
146
|
+
|
|
147
|
+
Базовый класс для сигналов только для чтения. Используется внутри `signalize` и для создания кастомных сигналов.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { ReadonlySignal } from '@fozy-labs/rx-toolkit';
|
|
151
|
+
|
|
152
|
+
const customSignal = new ReadonlySignal((subscriber) => {
|
|
153
|
+
// Логика подписки
|
|
154
|
+
subscriber.next(initialValue);
|
|
155
|
+
return () => {
|
|
156
|
+
// Cleanup
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### LocalState
|
|
162
|
+
|
|
163
|
+
Сигнал, который автоматически синхронизируется с `localStorage`.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { z } from 'zod/v4';
|
|
167
|
+
import { LocalState } from '@fozy-labs/rx-toolkit';
|
|
168
|
+
|
|
169
|
+
enum FILTER {
|
|
170
|
+
ALL = 'all',
|
|
171
|
+
CHANNELS = 'channels',
|
|
172
|
+
CHATS = 'chats',
|
|
173
|
+
MEETINGS = 'meetings',
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const selectedFilter$ = LocalState.create({
|
|
177
|
+
key: 'memberships-list-selected-filter',
|
|
178
|
+
defaultValue: FILTER.ALL,
|
|
179
|
+
zodSchema: z.nativeEnum(FILTER), // Опционально: валидация через Zod
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Использование
|
|
183
|
+
console.log(selectedFilter$()); // Значение из localStorage или FILTER.ALL
|
|
184
|
+
selectedFilter$.set(FILTER.CHANNELS); // Сохраняется в localStorage
|
|
185
|
+
|
|
186
|
+
function logout() {
|
|
187
|
+
selectedFilter$.clear(); // Удаляет значение из localStorage (сбрасывает на defaultValue)
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Опции LocalState:**
|
|
192
|
+
- `key` — ключ для localStorage
|
|
193
|
+
- `defaultValue` — значение по умолчанию
|
|
194
|
+
- `zodSchema` — опциональная Zod-схема для валидации
|
|
195
|
+
- `userId` — опциональный идентификатор пользователя для изоляции данных
|
|
196
|
+
- `checkEffect` — функция валидации значения
|
|
197
|
+
- `devtoolsOptions` — настройки для devtools
|
|
198
|
+
- `driver` — драйвер для хранения (по умолчанию localStorage, можно заменить на кастомный драйвер)
|
|
199
|
+
|
|
200
|
+
## Операторы
|
|
201
|
+
|
|
202
|
+
### signalize
|
|
203
|
+
|
|
204
|
+
Преобразует RxJS Observable в Signal. Позволяет использовать любой Observable как реактивный сигнал.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { interval, startWith } from 'rxjs';
|
|
208
|
+
import { signalize, Effect } from '@fozy-labs/rx-toolkit';
|
|
209
|
+
|
|
210
|
+
// Создаем Observable, который эмитит значение каждую секунду
|
|
211
|
+
const timer$ = interval(1000).pipe(
|
|
212
|
+
startWith(0),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Преобразуем Observable в Signal
|
|
216
|
+
const tick$ = signalize(timer$);
|
|
217
|
+
|
|
218
|
+
// Теперь можно использовать tick$ как обычный Signal
|
|
219
|
+
new Effect(() => {
|
|
220
|
+
console.log(`Timer: ${tick$.get()}`);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Доступ к значению без подписки
|
|
224
|
+
console.log(tick$.peek());
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Батчинг обновлений (Batcher)
|
|
228
|
+
|
|
229
|
+
RxSignals автоматически группирует множественные обновления сигналов в один цикл обновления. Это обеспечивает:
|
|
230
|
+
- Консистентность состояния
|
|
231
|
+
- Оптимальную производительность
|
|
232
|
+
- Предсказуемый порядок выполнения эффектов
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const a = Signal.state(1);
|
|
236
|
+
const b = Signal.state(2);
|
|
237
|
+
const sum = Signal.compute(() => a() + b());
|
|
238
|
+
|
|
239
|
+
new Effect(() => {
|
|
240
|
+
console.log(`Sum: ${sum()}`);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Оба изменения обрабатываются в одном батче
|
|
244
|
+
Batcher.run(() => {
|
|
245
|
+
a.set(10);
|
|
246
|
+
b.set(20);
|
|
247
|
+
});
|
|
248
|
+
// Effect выведет: "Sum: 30" (один раз, а не два)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Интеграция с RxJS
|
|
252
|
+
|
|
253
|
+
Сигналы полностью совместимы с RxJS. Каждый сигнал предоставляет `obs` — стандартный RxJS Observable:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { filter, take, debounceTime } from 'rxjs';
|
|
257
|
+
import { Signal, Computed, signalize } from '@fozy-labs/rx-toolkit';
|
|
258
|
+
|
|
259
|
+
const clicks = Signal.state(0);
|
|
260
|
+
|
|
261
|
+
// Используем RxJS операторы
|
|
262
|
+
const tenClicks$ = clicks.obs.pipe(
|
|
263
|
+
filter(value => value === 10),
|
|
264
|
+
take(1)
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
tenClicks$.subscribe(() => {
|
|
268
|
+
console.log('Reached 10 clicks!');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Или наоборот - превращаем Observable в Signal
|
|
272
|
+
const debouncedClicks$ = signalize(
|
|
273
|
+
clicks.obs.pipe(
|
|
274
|
+
debounceTime(300)
|
|
275
|
+
)
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
// Теперь debouncedClicks$ можно использовать в Computed/Effect
|
|
279
|
+
const doubled = Signal.compute(() => debouncedClicks$() * 2);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Devtools
|
|
283
|
+
|
|
284
|
+
Сигналы поддерживают интеграцию с Redux DevTools для отладки:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { Signal } from '@fozy-labs/rx-toolkit';
|
|
288
|
+
|
|
289
|
+
// С именем для devtools
|
|
290
|
+
const count$ = Signal.state(0, 'counter');
|
|
291
|
+
|
|
292
|
+
// Или с расширенными опциями
|
|
293
|
+
const user$ = Signal.state(null, {
|
|
294
|
+
isDisabled: false, // Отключить отслеживание в devtools
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## React интеграция
|
|
299
|
+
|
|
300
|
+
См. [React интеграция](../usage/react/README.md) для подробной информации о том, как использовать RxSignals в React приложениях.
|