Ngrx中store数据未就绪时Resolver执行失败 如何重试或预加载数据
问题根因
Angular 路由 Resolver 的执行优先级高于 @ngrx/router-store 派发的ROUTER_REQUEST/ROUTER_NAVIGATION等路由action,直接访问详情页链接时,Resolver会先触发执行查询详情数据,此时加载基础数据的Effect还未执行,store中无对应的comics列表数据,Resolver的filter条件永远不满足,导致页面永久卡住。
解决方案一:改造Resolver,等待基础数据加载完成(推荐)
直接在Resolver中先等待基础数据加载完成,再执行后续的详情查询逻辑,无需修改现有Effect逻辑,改造成本最低:
export class ComicDetailResolver implements Resolve<ComicDetail> { constructor(private store: Store<State>) { } resolve(): Observable<ComicDetail> { // 先等待基础数据加载完成 return this.store.pipe( select(isLoaded), filter(loaded => !!loaded), // 等isLoaded变为true才往下走 take(1), // 再拿路由ID触发详情查询 switchMap(() => this.store.pipe(select(getRouteID))), map((routeId: string) => { this.store.dispatch(AppActions.getComicDetail({ routeId })); return routeId; }), // 等待详情数据返回 switchMap(() => this.store.pipe(select(getItem))), filter((comic: ComicDetail) => !!comic), first() ); } }
解决方案二:应用初始化阶段预加载基础数据
如果希望所有路由执行前都保证基础数据可用,可以通过Angular的APP_INITIALIZER钩子在应用启动时就加载基础数据,优先级高于路由解析:
- 首先在AppModule中添加初始化provider:
import { APP_INITIALIZER } from '@angular/core'; // 初始化加载函数 export function preloadBaseData(store: Store<State>): () => Observable<any> { return () => store.pipe( select(isLoaded), tap(loaded => { if (!loaded) { // 新增一个触发加载数据的action,原有加载Effect逻辑可以复用 store.dispatch(AppActions.triggerLoadData()); } }), filter(loaded => !!loaded), take(1) ); } // AppModule的providers中添加配置 @NgModule({ // ... 其他配置保持不变 providers: [ { provide: Window, useValue: window }, { provide: APP_INITIALIZER, useFactory: preloadBaseData, deps: [Store], multi: true } ], }) export class AppModule { }
- 可以删掉原来监听路由action触发加载数据的Effect,或者保留做降级兼容。
原有重试代码无效的原因
- Resolver要求必须返回
Observable/Promise,你之前的代码直接调用了subscribe,没有返回流对象,路由会直接判定Resolver执行完成,不会等待异步逻辑 throwError用法错误,throwError需要接收错误值,不需要包裹of(),你之前的写法不会触发流报错,retry操作符根本不会被触发- 重试逻辑没有加延迟,三次重试会瞬间跑完,还是等不到Effect加载数据完成
内容的提问来源于stack exchange,提问作者RasMason
相关产品推荐
相关产品推荐

