@fozy-labs/rx-toolkit
v0.5.4
Published
Набор инструментов для реактивного управления состоянием, построенный поверх RxJS.
Maintainers
Readme
RxToolkit
Фреймворк агностик набор инструментов для реактивного управления состоянием, построенный поверх RxJS.
📦 Установка
npm install @fozy-labs/rx-toolkit rxjs🎯 Цель
RxJS действительно мощный инструмент реактивного программирования, он удобен когда мы работаем с потоком событий, но когда речь заходит о состоянии приложения, из-за асинхронной природы rx'а, его использование становится сложным и громоздким, не говоря уже о кешировании данных (хотя некоторые разработчики "продают" rxjs, как альтернативу Query библиотекам, на самом деле реализация подобного функционала выльется в создание отдельной библиотеки).
RxToolkit решает эти проблемы, предоставляя свою реализацию сигналов и кеш-менеджера.
✨ Особенности
- 🧩 Реактивные примитивы — Привычные Signal, Computed и Effect.
- 🔧 Framework-agnostic — Стройте систему и описывайте логику в изолированном месте.
- ⚡ Built on RxJS — Наследует всю мощь RxJS.
- 💾 Кеш-менеджер — Предоставляет Query реализацию для работы с данными.
- 🧪 Query v2 (experimental) — Переработанный кеш-менеджер с machine states, плагинами и SSR snapshots.
- 🔷 TypeScript-first — Полная типизация.
- 🔗 Интеграция с фреймворками — Как и RxJS напрямую работает в Angular, Svelte и SolidJS. Поставляется с React-хуками из коробки.
📚 Документация
- RxSignals - реактивные примитивы
- RxQuery - кеш-менеджер для работы с данными
- RxQuery v2 - кеш-менеджер v2 (experimental)
- React - интеграция с React
- Devtools - инструменты разработчика
- DefaultOptions - глобальные настройки
🌟 Примеры
Создаем сигнал
// Описываем логику в обычном JavaScript
const count$ = Signal.state(0);
const doubled$ = Signal.compute(() => count$() * 2);
const increment = () => count$.set(count$() + 1);Подключаем к фреймворку
// React
const count = useSignal(count$);
// Angular signal
public readonly count = toSignal(count$.obs);
// Angular pipe
{{ count$.obs | async }}
// SolidJS
const count = from(count$.obs)
// Svelte
$: count = count$.obs;Работаем с RxJS
// Создаем Observable
const clicker$ = fromEvent(document, 'click').pipe(
debounceTime(300),
scan(count => count + 1, 0),
startWith(0),
);
// Получаем сигнал из Observable
const clickCount$ = signalize(clicker$);
const doubled$ = Signal.compute(() => clickCount$() * 2);
console.log(doubled$()); // Всегда актуальное значение
// Или наоборот, получаем событие из сигнала
const on10click$ = doubled$.obs.pipe(
filter(value => value === 10),
take(1)
);
const sub = on10click$.subscribe(() => {
console.log('Great! That you first reached 10 clicks!');
});
// Не забываем отписаться
sub.unsubscribe();RxQuery (Корзина покупок)
const getCart = createResource({
queryFn: fetchCart,
});
const toggleCardItem = createCommand({
queryFn: fetchToggleCardItem,
link(add) {
add({
resource: getCart,
forwardArgs: () => undefined,
optimisticUpdate: ({ draft, args }) => {
const item = draft.items.find(i => i.id === args.id);
if (!item) return;
item.enabled = args.enabled;
}
})
}
});
function ShoppingCart() {
const cartQuery = useResourceAgent(getCart);
const [toggleItem] = useCommandAgent(toggleCardItem);
const cart = cartQuery.data;
return (
<Container isLoading={cartQuery.isLoading}>
{cart?.items.map(item => (
<CartItem
key={item.id}
item={item}
onToggle={() => toggleItem({ id: item.id, enabled: !item.enabled })}
/>
))}
</Container>
);
}