Angular借助RxJS实现Store定时重取数据仅首次生效问题
问题根源
- 订阅逻辑位置错误:你将
reloadLeaderboards$的订阅逻辑放在了updateLeaderBoard方法中,每次调用该方法都会新建一个订阅,但从未主动给reloadLeaderboards$推送新的事件,除了首次可能的默认触发外,后续不会执行switchMap内的请求逻辑。 - 重复调用初始化逻辑:组件定时器每次都调用
store.init(),而init方法只需要执行一次初始化操作,重复调用只会创建多余的无用订阅,还会造成内存泄漏。 - 事件触发逻辑缺失:你没有实现主动推送刷新事件的逻辑,只是重复创建订阅,自然无法触发新的接口请求。
修复方案
1. 调整Store代码
将订阅逻辑移到构造函数中仅初始化一次,新增主动触发刷新的方法:
@Injectable() export class LeaderboardStore extends Store<LeaderboardStoreState> implements OnDestroy { private storeRequestStateUpdater: StoreRequestStateUpdater; private ngUnsubscribe$: Subject<undefined> = new Subject(); private reloadLeaderboards$: Subject<CompetitionFilteringOptions> = new Subject(); constructor(private endpoint: LeaderboardEndpoint) { super(new LeaderboardStoreState()); this.storeRequestStateUpdater = endpointHelpers.getStoreRequestStateUpdater(this); // 订阅逻辑仅初始化一次 this.initReloadListener(); } private initReloadListener(): void { this.reloadLeaderboards$ .pipe( switchMap((filterBy: CompetitionFilteringOptions) => { console.log('switch maps') return this.endpoint.listLeaderboards(this.storeRequestStateUpdater, filterBy); }), tap((leaderboards: any) => { // 建议先深拷贝返回数据再修改,避免引用类型导致的缓存问题 const clonedLeaderboards = JSON.parse(JSON.stringify(leaderboards)); const currentUser = clonedLeaderboards[0].entries.find(entry => entry.userName === 'legenden420'); currentUser.me = true; const currentUserPoints = currentUser.points; this.setState({ ...this.state, leaderboards: clonedLeaderboards, currentUserPoints }); }), retry(), takeUntil(this.ngUnsubscribe$) ) .subscribe(); } // 对外暴露的刷新触发方法 triggerRefresh(filterBy: CompetitionFilteringOptions): void { console.log('reloaded leaderboard'); this.reloadLeaderboards$.next(filterBy); } // 首次初始化调用,可保留 init(initialFilter: CompetitionFilteringOptions): void { this.triggerRefresh(initialFilter); } ngOnDestroy(): void { this.ngUnsubscribe$.next(); this.ngUnsubscribe$.complete(); } }
2. 调整组件代码
定时器仅调用刷新方法,init只在组件初始化时执行一次:
ngOnInit() { // 替换为你实际使用的初始过滤参数 const filterOptions = {} as CompetitionFilteringOptions; // 首次加载只调用一次 this.store.init(filterOptions); // 定时器实现定时刷新 this.timer$ = interval(5000); console.log('init timer'); this.subscription = this.timer$.subscribe(t => { console.log('updated nr ' + t); // 传入当前使用的过滤参数触发刷新 this.store.triggerRefresh(filterOptions); }); } // 组件销毁时清理订阅,避免内存泄漏 ngOnDestroy(): void { this.subscription?.unsubscribe(); }
额外排查点
如果修改后仍然返回旧数据,按以下顺序排查:
- 打开浏览器Network面板,确认每次触发刷新是否真的发出了新的HTTP请求,响应内容是否为最新数据
- 如果请求未发出,检查
listLeaderboards方法是否做了本地缓存,没有真实发起请求 - 如果请求已发出但响应是旧数据,可在请求头添加
Cache-Control: no-cache禁用浏览器缓存,或者给请求URL添加时间戳参数避免缓存 - 如果响应是新数据但状态未更新,检查
setState方法是否正确触发了状态变更通知
内容的提问来源于stack exchange,提问作者Kristoffer Tølbøll
相关产品推荐
相关产品推荐

