rcrx
Version:
Rcrx is a lightweight library that provides Rx.js utilities for React applications.
278 lines (192 loc) • 6.7 kB
Markdown
Lightweight RxJS hooks for React. 简洁的 React + RxJS Hook 工具库。
- English | [中文](
Rcrx provides a minimal set of hooks to seamlessly use RxJS Observables within React components:
- useObservable — read the latest value from an Observable as React state
- useSubscribe — subscribe to an Observable and map each emission using your function
Designed to be tiny, typed, and easy to adopt.
- Minimal API, zero configuration
- Automatic subscription management (unsubscribe on unmount)
- TypeScript support with generics
- Works with any RxJS Observable
- Keeps subscription stable when the mapping function changes (via ref)
## Installation
Using your favorite package manager:
```bash
# pnpm
pnpm add rcrx rxjs
# npm
npm install rcrx rxjs
# yarn
yarn add rcrx rxjs
```
Peer requirements:
- react >= 16.8.0 (hooks)
- rxjs (any 7+ recommended)
```tsx
import { useObservable } from "rcrx";
import { Observable } from "rxjs";
const counter$ = new Observable<number>((subscriber) => {
let n = 0;
const id = setInterval(() => subscriber.next(n++), 1000);
return () => clearInterval(id);
});
function Counter() {
const n = useObservable(counter$);
return <div>{n}</div>;
}
```
```tsx
import { useSubscribe } from "rcrx";
import { Observable } from "rxjs";
import { useState } from "react";
const counter$ = new Observable<number>((subscriber) => {
let n = 0;
const id = setInterval(() => subscriber.next(n++), 1000);
return () => clearInterval(id);
});
function Counter() {
const [n, setN] = useState(0);
useSubscribe(counter$, (v) => setN(v));
return <div>{n}</div>;
}
```
- Subscribes to the provided observable when the component mounts and returns the latest emitted value.
- Re-subscribes if the observable instance changes.
- Returns undefined until the first value is emitted.
observable: Observable<TValue>,
fn: (value: TValue) => TReturnValue,
defaultReturnValue?: TReturnValue
): TReturnValue | undefined
- Subscribes to the observable and applies fn to each emission; the hook returns the latest mapped result.
- Keeps the same subscription even if fn changes (fn is stored in a ref).
- Re-subscribes if the observable instance changes.
- Returns defaultReturnValue initially (or undefined if omitted).
Notes:
- For side-effects only, ignore the return value and use fn to update component state.
- For derived state, return a mapped value from fn and read the hook's return value.
## Advanced Usage
### Switching Observables
```tsx
function Dynamic({ source }: { source: Observable<number> }) {
const value = useObservable(source);
return <div>{value}</div>;
}
```
When the source prop changes to a different Observable instance, the hook will unsubscribe from the previous one and subscribe to the new one.
```tsx
function Mapped({ source }: { source: Observable<number> }) {
const doubled = useSubscribe(source, (v) => v * 2, 0);
return <div>{doubled}</div>;
}
```
Changing the mapping function identity won’t cause a re-subscription; only the Observable instance identity matters.
- Fully typed generics for both hooks
- No additional TS configuration required
- Build: `pnpm build` (outputs to `dist/` via tsup)
- Test: `pnpm test` or `pnpm test:watch` (Vitest)
ISC
---
Rcrx 提供一组极简的 React + RxJS Hook,帮助你在 React 组件中无缝使用 RxJS Observable:
- useObservable —— 将 Observable 的最新值作为组件状态读取
- useSubscribe —— 订阅 Observable,并用你的函数映射每次推送
- API 极简、开箱即用
- 自动管理订阅(组件卸载自动取消订阅)
- 完整的 TypeScript 支持
- 适配任意 RxJS Observable
- 当映射函数变更时保持订阅稳定(通过 ref)
```bash
pnpm add rcrx rxjs
npm install rcrx rxjs
yarn add rcrx rxjs
```
对等依赖与推荐:
- react >= 16.8.0(支持 Hook)
- rxjs(建议 7+)
```tsx
import { useObservable } from "rcrx";
import { Observable } from "rxjs";
const counter$ = new Observable<number>((subscriber) => {
let n = 0;
const id = setInterval(() => subscriber.next(n++), 1000);
return () => clearInterval(id);
});
function Counter() {
const n = useObservable(counter$);
return <div>{n}</div>;
}
```
```tsx
import { useSubscribe } from "rcrx";
import { Observable } from "rxjs";
import { useState } from "react";
const counter$ = new Observable<number>((subscriber) => {
let n = 0;
const id = setInterval(() => subscriber.next(n++), 1000);
return () => clearInterval(id);
});
function Counter() {
const [n, setN] = useState(0);
useSubscribe(counter$, (v) => setN(v));
return <div>{n}</div>;
}
```
- 组件挂载时订阅 Observable,并返回其最新推送值;当 Observable 实例变化时自动重新订阅。
- 在首次推送之前返回 undefined。
observable: Observable<TValue>,
fn: (value: TValue) => TReturnValue,
defaultReturnValue?: TReturnValue
): TReturnValue | undefined
- 订阅 Observable,并对每次推送应用 fn;Hook 返回最近一次映射的结果。
- fn 变更不会触发重新订阅(内部通过 ref 保存最新 fn)。
- 当 Observable 实例变化时会重新订阅。
- 初始返回 defaultReturnValue(未传入则为 undefined)。
提示:
- 仅用于副作用时,可忽略返回值,直接在 fn 中 setState。
- 需要派生状态时,可在 fn 中返回映射值,并通过 Hook 的返回值读取。
```tsx
function Dynamic({ source }: { source: Observable<number> }) {
const value = useObservable(source);
return <div>{value}</div>;
}
```
当 source 属性切换为新的 Observable 实例时,Hook 会自动取消之前的订阅,并订阅到新的实例。
```tsx
function Mapped({ source }: { source: Observable<number> }) {
const doubled = useSubscribe(source, (v) => v * 2, 0);
return <div>{doubled}</div>;
}
```
映射函数的引用发生变化不会触发重新订阅;只有 Observable 实例变化时才会。
- 构建:`pnpm build`(使用 tsup 输出到 `dist/`)
- 测试:`pnpm test` 或 `pnpm test:watch`(Vitest)
MIT