利用rxjs库的Subject多播解决在第一次订阅时进行初始化操作(如第一次订阅时从服务器获取数据)
rxjs 库中 Subject 类可以用于实现多播,其本身已经有多个实现(可参考【Rxjs】 - 解析四种主题Subject),但是都是需要手动操作Subject对象进行发布。
这里通过继承 BehaviorSubject(Subject的实现类,也可以直接继承Subject,但这里考虑到可能会用到初始值的时候)实现在第一次订阅(调用subscribe方法)时进行初始化(可以从服务器获取数据)。
第一步: InitSubject 类定义
import { ReplaySubject, Subscription, TeardownLogic } from 'rxjs';
/**
* 第一次订阅时执行初始化操作.
* <p>
* 和ReplaySubject一样,都会记录下最新的一个历史数据,以便有新订阅者时直接使用历史值。
* 区别在于:
* ReplaySubject 有初始值,第一次订阅会得到初始值,
* 而InitSubject第一次订阅则什么也不返回,但是第一次订阅会触发数据加载,加载成功后才对所有订阅者进行推送。
*/
export class InitSubject<T> extends ReplaySubject<T> {
/**
* 是否为第一次
*/
private first = true;
constructor(private init?: (subject: InitSubject<T>) => TeardownLogic) {
super(1);
}
// @ts-ignore 这里ts无法检查通过的原因是父类中存在一个弃用的重载函数所致
subscribe(next: (value: T) => void): Subscription {
if (this.first && this.init) {
this.first = false;
this.init(this);
}
return super.subscribe(next);
}
}
/**
* 一个便捷操作。创建InitSubject的同时也会返回一个重新加载函数(reload),以便在合适的时间重新加载数据。
*
* @param load 加载数据函数(InitSubject 并不知道如何加载数据,所以需要使用者提供)
*/
export function create<T>(load: (observer: (t: T) => void) => void): [InitSubject<T>, () => Promise<void>] {
/** 可观察对象 */
const subject$ = new InitSubject<T>(subject => load(v => subject.next(v)));
// 重新加载函数
const reload = async (): Promise<void> => {
return new Promise((resolve) => load(v => {
subject$.next(v);
resolve();
}));
};
return [subject$, reload];
}
第二步: 使用
import { create } from "@/services/InitSubject";
import request from "@/services/request";
/** 通过上面创建的 create 方法得到一个可观察对象(用于监听数据变化)和一个 reload 函数(用于需要重新加载数据时调用) */
export const [allAppSubject$, reloadAllApp] = create((observer: (item: App[]) => void) => {
request.get<App[]>('/api/v1/app/list').then(observer);
});
// React 中使用示例
function Test() {
const [app, setApps] = useState<App[]>([]);
useEffect(() => {
const subscription = allAppSubject$.subscribe(setApps);
// 注意一定要取消订阅,否则可能导致内存泄露或者出现意想不到的结果
return () => subscription.unsubscribe();
}, []);
console.log(app);
return (
<button onClick={() => reloadAllApp()}>重新加载</button>
)
}

浙公网安备 33010602011771号