Ngrx Effects:在switchMap中条件返回Action而非HTTP响应
在NgRx Effects中实现缓存逻辑:根据条件返回Action或HTTP请求
我来帮你搞定这个缓存需求!考虑到你用的是Angular 5,对应的NgRx版本应该是4.x左右,所以我会用适配旧版API的写法来给你演示。核心思路就是先从Store里读取缓存状态,根据缓存是否有效来决定直接返回成功Action还是发起HTTP请求。
核心步骤拆解
- 在Effect中通过
withLatestFrom操作符,获取当前Store里已缓存的合作伙伴数据
- 在Effect中通过
- 在
switchMap里做分支判断:如果缓存存在且有效,直接返回RequestPartnersSuccess动作;如果没有缓存,才调用服务发起HTTP请求
- 在
- 请求成功/失败时,依旧映射到对应的Success/Fail动作
具体代码实现
第一步:定义动作(Actions)
先确保你的动作类是按NgRx 4.x的方式定义的:
import { Action } from '@ngrx/store'; import { Partner } from './partner.model'; // 动作类型枚举 export enum PartnersActionTypes { REQUEST_PARTNERS = '[Partners] Request Partners', REQUEST_PARTNERS_SUCCESS = '[Partners] Request Partners Success', REQUEST_PARTNERS_FAIL = '[Partners] Request Partners Fail' } // 请求动作 export class RequestPartners implements Action { readonly type = PartnersActionTypes.REQUEST_PARTNERS; } // 请求成功动作 export class RequestPartnersSuccess implements Action { readonly type = PartnersActionTypes.REQUEST_PARTNERS_SUCCESS; constructor(public payload: Partner[]) {} } // 请求失败动作 export class RequestPartnersFail implements Action { readonly type = PartnersActionTypes.REQUEST_PARTNERS_FAIL; constructor(public payload: any) {} } // 导出动作联合类型 export type PartnersActions = RequestPartners | RequestPartnersSuccess | RequestPartnersFail;
第二步:定义状态结构(Reducer)
在Reducer里要包含标记缓存状态的字段,比如loaded(是否已加载过数据)、data(缓存的列表数据):
import { PartnersActions, PartnersActionTypes } from './partners.actions'; import { Partner } from './partner.model'; export interface PartnersState { data: Partner[]; loaded: boolean; // 标记是否已有缓存数据 loading: boolean; // 标记是否正在请求 error: any; } // 初始状态 const initialState: PartnersState = { data: [], loaded: false, loading: false, error: null }; // Reducer函数 export function partnersReducer(state = initialState, action: PartnersActions): PartnersState { switch (action.type) { case PartnersActionTypes.REQUEST_PARTNERS: return { ...state, loading: true, error: null }; case PartnersActionTypes.REQUEST_PARTNERS_SUCCESS: return { ...state, data: action.payload, loaded: true, loading: false }; case PartnersActionTypes.REQUEST_PARTNERS_FAIL: return { ...state, error: action.payload, loading: false }; default: return state; } } // 定义选择器,方便在Effect中获取状态 export const getPartnersState = (state: { partners: PartnersState }) => state.partners;
第三步:实现带缓存逻辑的Effect
这是核心部分,在Effect里加入缓存判断:
import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; import { switchMap, withLatestFrom, map, catchError } from 'rxjs/operators'; import { PartnersService } from './partners.service'; import * as PartnersActions from './partners.actions'; import * as fromRoot from '../reducers'; @Injectable() export class PartnersEffects { @Effect() loadPartners$ = this.actions$.pipe( // 监听请求合作伙伴的动作 ofType(PartnersActions.PartnersActionTypes.REQUEST_PARTNERS), // 同时获取当前Store里的合作伙伴状态 withLatestFrom(this.store.select(fromRoot.getPartnersState)), switchMap(([action, partnersState]) => { // 先判断是否已有有效缓存:loaded为true且数据不为空 if (partnersState.loaded && partnersState.data.length > 0) { // 缓存存在,直接返回成功动作,传入缓存数据 return of(new PartnersActions.RequestPartnersSuccess(partnersState.data)); } else { // 缓存不存在,发起HTTP请求 return this.partnersService.getPartners().pipe( // 请求成功,映射到成功动作 map(data => new PartnersActions.RequestPartnersSuccess(data)), // 请求失败,映射到失败动作 catchError(error => of(new PartnersActions.RequestPartnersFail(error))) ); } }) ); constructor( private actions$: Actions, private store: Store<fromRoot.State>, private partnersService: PartnersService ) {} }
额外优化建议
- 避免重复请求:如果页面上多次触发
REQUEST_PARTNERS动作,可以在switchMap里先判断partnersState.loading是否为true,如果是就返回EMPTY(需要导入EMPTYfrom rxjs),防止同时发起多个请求 - 缓存过期处理:如果需要缓存自动过期,可以在
PartnersState里加lastUpdated字段,记录最后一次请求的时间,在判断缓存时对比当前时间和lastUpdated的差值,超过设定时间就重新请求
这样当用户查看条目后返回主列表时,只要缓存存在且有效,就会直接用Store里的缓存数据,不用再发起HTTP请求啦!
内容的提问来源于stack exchange,提问作者Felipe Micali
相关产品推荐
相关产品推荐

