@fozy-labs/rx-toolkit 0.4.1
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 -0
- package/README.md +131 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/query/SKIP_TOKEN.d.ts +1 -0
- package/dist/query/SKIP_TOKEN.js +1 -0
- package/dist/query/api/DefaultOptions.d.ts +9 -0
- package/dist/query/api/DefaultOptions.js +9 -0
- package/dist/query/api/createDevtools.d.ts +1 -0
- package/dist/query/api/createDevtools.js +6 -0
- package/dist/query/api/createOperation.d.ts +7 -0
- package/dist/query/api/createOperation.js +6 -0
- package/dist/query/api/createResource.d.ts +3 -0
- package/dist/query/api/createResource.js +2 -0
- package/dist/query/api/createSubResource.d.ts +0 -0
- package/dist/query/api/createSubResource.js +1 -0
- package/dist/query/core/Opertation/Operation.d.ts +34 -0
- package/dist/query/core/Opertation/Operation.js +176 -0
- package/dist/query/core/Opertation/OperationAgent.d.ts +20 -0
- package/dist/query/core/Opertation/OperationAgent.js +54 -0
- package/dist/query/core/QueriesCache.d.ts +8 -0
- package/dist/query/core/QueriesCache.js +31 -0
- package/dist/query/core/Resource/Resource.d.ts +40 -0
- package/dist/query/core/Resource/Resource.js +154 -0
- package/dist/query/core/Resource/ResourceAgent.d.ts +34 -0
- package/dist/query/core/Resource/ResourceAgent.js +87 -0
- package/dist/query/core/Resource/ResourceRef.d.ts +18 -0
- package/dist/query/core/Resource/ResourceRef.js +52 -0
- package/dist/query/core/SharedOptions.d.ts +5 -0
- package/dist/query/core/SharedOptions.js +4 -0
- package/dist/query/index.d.ts +7 -0
- package/dist/query/index.js +7 -0
- package/dist/query/lib/IndirectMap.d.ts +18 -0
- package/dist/query/lib/IndirectMap.js +85 -0
- package/dist/query/lib/ReactiveCache.d.ts +77 -0
- package/dist/query/lib/ReactiveCache.js +96 -0
- package/dist/query/lib/shallowEqual.d.ts +1 -0
- package/dist/query/lib/shallowEqual.js +21 -0
- package/dist/query/react/useOperationAgent.d.ts +9 -0
- package/dist/query/react/useOperationAgent.js +22 -0
- package/dist/query/react/useResourceAgent.d.ts +9 -0
- package/dist/query/react/useResourceAgent.js +25 -0
- package/dist/query/types/Operation.types.d.ts +91 -0
- package/dist/query/types/Operation.types.js +1 -0
- package/dist/query/types/Resource.types.d.ts +90 -0
- package/dist/query/types/Resource.types.js +1 -0
- package/dist/query/types/shared.types.d.ts +4 -0
- package/dist/query/types/shared.types.js +1 -0
- package/dist/react-hooks/index.d.ts +5 -0
- package/dist/react-hooks/index.js +5 -0
- package/dist/react-hooks/useConstant.d.ts +4 -0
- package/dist/react-hooks/useConstant.js +19 -0
- package/dist/react-hooks/useEventHandler.d.ts +1 -0
- package/dist/react-hooks/useEventHandler.js +6 -0
- package/dist/react-hooks/useObservable.d.ts +12 -0
- package/dist/react-hooks/useObservable.js +48 -0
- package/dist/react-hooks/useSignal.d.ts +2 -0
- package/dist/react-hooks/useSignal.js +12 -0
- package/dist/react-hooks/useSyncObservable.d.ts +16 -0
- package/dist/react-hooks/useSyncObservable.js +52 -0
- package/dist/signal/base/Batcher.d.ts +7 -0
- package/dist/signal/base/Batcher.js +21 -0
- package/dist/signal/base/Computed.d.ts +10 -0
- package/dist/signal/base/Computed.js +26 -0
- package/dist/signal/base/Effect.d.ts +12 -0
- package/dist/signal/base/Effect.js +69 -0
- package/dist/signal/base/ReadonlySignal.d.ts +12 -0
- package/dist/signal/base/ReadonlySignal.js +20 -0
- package/dist/signal/base/Signal.d.ts +30 -0
- package/dist/signal/base/Signal.js +44 -0
- package/dist/signal/base/SyncObservable.d.ts +5 -0
- package/dist/signal/base/SyncObservable.js +18 -0
- package/dist/signal/base/Tracker.d.ts +5 -0
- package/dist/signal/base/Tracker.js +7 -0
- package/dist/signal/base/index.d.ts +8 -0
- package/dist/signal/base/index.js +8 -0
- package/dist/signal/base/types.d.ts +15 -0
- package/dist/signal/base/types.js +1 -0
- package/dist/signal/extends/LocalSignal.d.ts +24 -0
- package/dist/signal/extends/LocalSignal.js +66 -0
- package/dist/signal/extends/index.d.ts +1 -0
- package/dist/signal/extends/index.js +1 -0
- package/dist/signal/index.d.ts +3 -0
- package/dist/signal/index.js +3 -0
- package/dist/signal/operators/batch.d.ts +2 -0
- package/dist/signal/operators/batch.js +27 -0
- package/dist/signal/operators/filterUpdates.d.ts +5 -0
- package/dist/signal/operators/filterUpdates.js +18 -0
- package/dist/signal/operators/index.d.ts +4 -0
- package/dist/signal/operators/index.js +4 -0
- package/dist/signal/operators/mapSignals.d.ts +3 -0
- package/dist/signal/operators/mapSignals.js +10 -0
- package/dist/signal/operators/signalize.d.ts +3 -0
- package/dist/signal/operators/signalize.js +6 -0
- package/docs/query/README.md +224 -0
- package/docs/signals/README.md +119 -0
- package/docs/usage/react/README.md +144 -0
- package/package.json +57 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# RxSignals
|
|
2
|
+
|
|
3
|
+
RxSignals — это реактивная система управления состоянием, вдохновленная современными фреймворками типа SolidJS и Angular Signals. Она предоставляет эффективные инструменты для создания реактивных приложений.
|
|
4
|
+
|
|
5
|
+
## Основные концепции
|
|
6
|
+
|
|
7
|
+
### Signal
|
|
8
|
+
|
|
9
|
+
Базовый класс для создания реактивных сигналов.
|
|
10
|
+
|
|
11
|
+
**Пример использования:**
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
const name = new Signal('John');
|
|
15
|
+
const age = new Signal(25);
|
|
16
|
+
|
|
17
|
+
// Подписка на изменения
|
|
18
|
+
const subscription = name.subscribe(newName => {
|
|
19
|
+
console.log(`Name changed to: ${newName}`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
name.value = 'Jane'; // Выведет: "Name changed to: Jane"
|
|
23
|
+
|
|
24
|
+
// Отписка
|
|
25
|
+
subscription.unsubscribe();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Computed
|
|
29
|
+
|
|
30
|
+
Создает вычисляемое значение, которое автоматически обновляется при изменении зависимостей.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const firstName = new Signal('John');
|
|
34
|
+
const lastName = new Signal('Doe');
|
|
35
|
+
|
|
36
|
+
const fullName = new Computed(() => `${firstName.value} ${lastName.value}`);
|
|
37
|
+
|
|
38
|
+
console.log(fullName.value); // "John Doe"
|
|
39
|
+
|
|
40
|
+
firstName.value = 'Jane';
|
|
41
|
+
console.log(fullName.value); // "Jane Doe"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Effect
|
|
45
|
+
|
|
46
|
+
Создает эффект, который выполняется при изменении используемых сигналов.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const count = new Signal(0);
|
|
50
|
+
const message = new Signal('Hello');
|
|
51
|
+
|
|
52
|
+
const sub = new Effect(() => {
|
|
53
|
+
// Выведет: "Hello: 0" при инициализации
|
|
54
|
+
console.log(`${message.value}: ${count.value}`);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
count.value = 1; // Выведет: "Hello: 1"
|
|
58
|
+
message.value = 'Hi'; // Выведет: "Hi: 1"
|
|
59
|
+
|
|
60
|
+
// Остановка эффекта
|
|
61
|
+
sub.unsubscribe();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### LocalSignal
|
|
65
|
+
|
|
66
|
+
Сигнал, который синхронизируется с локальным хранилищем (localStorage).
|
|
67
|
+
// TODO убрать zod из core, добавить возможность передавать "driver".
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { LocalSignal } from '@fozy-labs/rx-toolkit';
|
|
71
|
+
|
|
72
|
+
const selectedFilter$ = new LocalSignal({
|
|
73
|
+
key: 'projects-list-selected-filter',
|
|
74
|
+
defaultValue: FILTER.ALL,
|
|
75
|
+
zodSchema: z.enum(FILTER),
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Operators (Операторы)
|
|
80
|
+
|
|
81
|
+
### batched
|
|
82
|
+
Группирует несколько обновлений в одну транзакцию для оптимизации производительности.
|
|
83
|
+
// TODO: проверить, тк не используется в fozy, добавить пример использования
|
|
84
|
+
|
|
85
|
+
### mapSignals
|
|
86
|
+
// TODO: добавить описание
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### signalize
|
|
90
|
+
|
|
91
|
+
Преобразует Observable в Signal.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { signalize } from '@fozy-labs/rx-toolkit';
|
|
95
|
+
import { interval } from 'rxjs';
|
|
96
|
+
import { Effect } from "src/signal/base/Effect";
|
|
97
|
+
|
|
98
|
+
// Создаем Observable, который эмитит значение каждую секунду
|
|
99
|
+
const timer$ = interval(1000).pipe(
|
|
100
|
+
startWith(0),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Преобразуем Observable в Signal
|
|
104
|
+
const tick$ = signalize(timer$);
|
|
105
|
+
|
|
106
|
+
// Теперь можно использовать timerSignal как обычный Signal
|
|
107
|
+
new Effect(() => {
|
|
108
|
+
console.log(`Timer: ${tick$.value}`);
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### filterUpdates
|
|
113
|
+
|
|
114
|
+
Фильтрует обновления сигнала.
|
|
115
|
+
// TODO: добавить пример использования, подумать над целесообразностью
|
|
116
|
+
|
|
117
|
+
## React Integration
|
|
118
|
+
|
|
119
|
+
См. [React интеграция](../usage/react/README.md) для подробной информации о том, как использовать RxSignals в React приложениях.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# React интеграция
|
|
2
|
+
|
|
3
|
+
RxToolkit предоставляет набор React хуков для эффективной интеграции с реактивными системами библиотеки. Все хуки оптимизированы для минимальных ре-рендеров и максимальной производительности.
|
|
4
|
+
|
|
5
|
+
## Основные хуки
|
|
6
|
+
|
|
7
|
+
### useSignal
|
|
8
|
+
|
|
9
|
+
Подписывается на изменения сигнала и возвращает текущее значение.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { useSignal } from '@fozy-labs/rx-toolkit';
|
|
13
|
+
|
|
14
|
+
function Counter() {
|
|
15
|
+
const count = useSignal(countSignal);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div>
|
|
19
|
+
<p>Count: {count}</p>
|
|
20
|
+
<button onClick={() => countSignal.value++}>
|
|
21
|
+
Increment
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Особенности:**
|
|
29
|
+
- Автоматическая подписка и отписка
|
|
30
|
+
- Использует `useSyncExternalStore` для оптимальной производительности
|
|
31
|
+
- Не вызывает ре-рендер, если значение не изменилось
|
|
32
|
+
|
|
33
|
+
### useObservable
|
|
34
|
+
|
|
35
|
+
Подписывается на RxJS Observable и возвращает текущее значение.
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { useObservable } from '@fozy-labs/rx-toolkit';
|
|
39
|
+
import { interval, map } from 'rxjs';
|
|
40
|
+
|
|
41
|
+
const timer$ = interval(1000).pipe(
|
|
42
|
+
map(count => `Timer: ${count}`)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
function Timer() {
|
|
46
|
+
const timerValue = useObservable(timer$, 'Timer: 0');
|
|
47
|
+
|
|
48
|
+
return <div>{timerValue}</div>;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Параметры:**
|
|
53
|
+
- `observable$` — RxJS Observable для подписки
|
|
54
|
+
- `initialValue` — начальное значение до первой эмиссии
|
|
55
|
+
|
|
56
|
+
### useSyncObservable
|
|
57
|
+
|
|
58
|
+
Синхронная версия `useObservable` для Observable, которые могут выдать значение немедленно.
|
|
59
|
+
Если Observable не может выдать значение сразу, будет выброшена ошибка.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { useSyncObservable } from '@fozy-labs/rx-toolkit';
|
|
63
|
+
import { BehaviorSubject } from 'rxjs';
|
|
64
|
+
|
|
65
|
+
const data$ = new BehaviorSubject('initial data');
|
|
66
|
+
|
|
67
|
+
function DataComponent() {
|
|
68
|
+
const data = useSyncObservable(data$);
|
|
69
|
+
|
|
70
|
+
return <div>Data: {data}</div>;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Отличия от useObservable:**
|
|
75
|
+
- Не требует начального значения
|
|
76
|
+
- Получает значение синхронно при первом рендере
|
|
77
|
+
- Подходит для BehaviorSubject и других синхронных Observable
|
|
78
|
+
|
|
79
|
+
## RxQuery хуки
|
|
80
|
+
|
|
81
|
+
### useResourceAgent
|
|
82
|
+
|
|
83
|
+
Подписывается на состояние ресурса и автоматически инициирует запрос.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { useResourceAgent, SKIP } from '@fozy-labs/rx-toolkit';
|
|
87
|
+
import { userResource } from '../api/userResource.ts';
|
|
88
|
+
|
|
89
|
+
// Автоматическая инициация
|
|
90
|
+
function UserProfile({ userId }: { userId: string | null }) {
|
|
91
|
+
const userState = useResourceAgent(userResource, userId ? { id: userId } : SKIP);
|
|
92
|
+
|
|
93
|
+
if (userState.isLoading || !userId) return <div>Загрузка...</div>;
|
|
94
|
+
if (userState.isError) return <div>Ошибка: {userState.error?.message}</div>;
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div>
|
|
98
|
+
<h1>{userState.data?.name}</h1>
|
|
99
|
+
<p>{userState.data?.email}</p>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Особенности:**
|
|
106
|
+
- Автоматическая подписка на состояние ресурса
|
|
107
|
+
- Умная инициация: не повторяет запрос для тех же аргументов
|
|
108
|
+
- Поддержка SKIP токена для ручного управления
|
|
109
|
+
|
|
110
|
+
### useOperationAgent
|
|
111
|
+
|
|
112
|
+
Подписывается на состояние операции для отслеживания выполнения мутаций.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { useOperationAgent } from '@fozy-labs/rx-toolkit';
|
|
116
|
+
import { updateUserOperation } from '../api/updateUserOperation.ts';
|
|
117
|
+
import { User } from '../types/user.ts';
|
|
118
|
+
|
|
119
|
+
function EditUserForm({ user }: { user: User }) {
|
|
120
|
+
const [updateUser, updateUserQuery] = useOperationAgent(updateUserOperation);
|
|
121
|
+
|
|
122
|
+
const handleSubmit = async (event: React.FormEvent) => {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
const formData = new FormData(event.target as HTMLFormElement);
|
|
125
|
+
try {
|
|
126
|
+
await agent.initiate({ id: user.id, data: formData });
|
|
127
|
+
alert('Пользователь обновлен');
|
|
128
|
+
} catch (error) {
|
|
129
|
+
alert('Ошибка при обновлении пользователя');
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<form onSubmit={handleSubmit}>
|
|
135
|
+
<input name="name" defaultValue={user.name} />
|
|
136
|
+
<input name="email" defaultValue={user.email} />
|
|
137
|
+
<button type="submit" disabled={updateUserQuery.isLoading}>
|
|
138
|
+
{updateUserQuery.isLoading ? 'Обновление...' : 'Обновить'}
|
|
139
|
+
</button>
|
|
140
|
+
{updateUserQuery.isError && <p>Ошибка: {updateUserQuery.error?.message}</p>}
|
|
141
|
+
</form>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fozy-labs/rx-toolkit",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "rimraf ./dist && tsc && tsc-alias",
|
|
9
|
+
"build:watch": "rimraf ./dist && (concurrently \"tsc -w\" \"tsc-alias -w\")",
|
|
10
|
+
"ts-check": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@reatom/devtools": "0.13.1"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"rimraf": "6.0.1",
|
|
17
|
+
"tsc-alias": "^1.8.16",
|
|
18
|
+
"tsconfig-paths": "4.2.0",
|
|
19
|
+
"concurrently": "9.2.0",
|
|
20
|
+
"typescript": "5.9.2",
|
|
21
|
+
"zod": "4.1.11"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": "19.1.1",
|
|
25
|
+
"rxjs": "7.8.2",
|
|
26
|
+
"zod": "4.1.11"
|
|
27
|
+
},
|
|
28
|
+
"description": "Набор инструментов для реактивного управления состоянием, построенный поверх RxJS.",
|
|
29
|
+
"author": "Vladimir Panev <vova6255@gmail.com>",
|
|
30
|
+
"homepage": "https://github.com/fozy-labs/rx-toolkit",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"keywords": ["react", "rxjs", "toolkit", "state-management"],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/fozy-labs/rx-toolkit.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/fozy-labs/rx-toolkit/issues"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"docs",
|
|
43
|
+
"README.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"import": "./dist/index.js",
|
|
49
|
+
"types": "./dist/index.d.ts"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": false,
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"preferGlobal": false
|
|
57
|
+
}
|