angular @ngrx - redux 我应该在哪里处理不改变状态的导出操作

angular @ngrx - redux where should i handle actions like export that don't change the state

我有一个带有 "Export" 按钮的应用程序。点击后,APP从服务器获取一个大文本文件,并 使用 HTML5 blob API 到 "save as" 以便用户可以将其保存到他的磁盘

为了在dispatch export action中开始导出操作

this.store.dispatch(new scanListsActions.Export(id));

此操作随后由 "effects" 代码处理,该代码转到后端服务器以获取文件。 完成后,将调度 ExportSuccess 操作。

现在,我不想将文件内容存储在商店中,因为它非常大, 另一方面,我需要视图中的数据才能将其发送给用户。 目前你可以看到我在 reducer 代码(下面)中将它发送给了用户。

通常是如何完成的?将文件发送给用户的地方在哪里?

import * as fileSaver from 'file-saver'; // npm i --save file-saver

export function reducer(state: ScanListsState = initialState, action: ScanListsActions): ScanListsState {
    switch (action.type) {
        case ScanListsActionTypes.Export:
            return {
                ...state,
                loading: true
            };

        case ScanListsActionTypes.ExportSuccess:

       // these 2 lines below send the file to the user (starts a download)
       // where should i put this code. I don't think the reducer is the place for it

            const blob = new Blob([action.payload.data], { type: 'text/plain; charset=utf-8' });
            fileSaver.saveAs(blob, 'destination_list.txt');

            return {
                ...state,
                loading: false,
                error: null
            };

        case ScanListsActionTypes.GeneralFail:
            return {
                ...state,
                loading: false,
                error: action.payload
            };
        default:
            return state;
    }
}

效果代码

@Injectable()
export class ScanListsEffects {

    constructor(private scanListService: ScanListService,
        private actions$: Actions) { }



    @Effect()
    exportScanList$: Observable<Action> = this.actions$.pipe(
        ofType(scanListsActions.ScanListsActionTypes.Export),
        mergeMap((action: scanListsActions.Export) =>
            this.scanListService.getScanList(action.payload).pipe(
                map(sList => (new scanListsActions.ExportSuccess(sList))),
                catchError(err => of(new scanListsActions.GeneralFail(err)))
            )
        )
    );
}

你是对的,你不应该在减速器中这样做(至少我不会)。您应该在组件本身中执行此操作。

你应该以 url 或任何键的形式将 action.payload.data 存储在 reducer 中并对其做出反应。

像这样:

您的减速器状态应如下所示:

export interface ScanListsState { 
 url: string; // (I assume string),
 loading: boolean;
 error: any; // You can put whatever interface you want
}

除了 ScanListsActionTypes.ExportSuccess: return:

之外,你的 reducer 中的其他一切都很好
return {
  ...state,
  url: action.payload.data,
  loading: false,
  error: null,
};

然后在你的组件中

ngOnInit() {
  this.reactToURLChangesOfExport();
}
exportClicked() {
  this.store.dispatch(new scanListsActions.Export(id));
}
reactToURLChangesOfExport() {
  this.store.pipe(
   select(YourSelectorForTheSliceOfTheURL), // make a selector that will react to changes to the URL property of the slice of data in the store.
   // put a takeUntil or however you want to destroy this subscription so it prevents memory leaks
  ).subscribe(url => {
    const blob = new Blob([url], { type: 'text/plain; charset=utf-8' });
    fileSaver.saveAs(blob, 'destination_list.txt');
  })
}

您应该拨打服务电话以进行保存,就像您为阅读所做的那样。实际上,您会调用该服务:

类似于:

    @Effect()
    fileSaveExportScanList$: Observable<Action> = this.actions$.pipe(
        ofType(scanListsActions.ScanListsActionTypes.ExportSuccess),
        mergeMap((action: scanListsActions.ExportSuccess) =>
            this.scanListService.saveScanList(action.payload).pipe(
                map (() => (new scanListsActions.SaveSuccess())),
                catchError(err => of(new scanListsActions.GeneralFail(err)))
            )
        )
    );

然后拨打服务电话'saveScanList'

     saveScanList(data: any){
            const blob = new Blob([data], { type: 'text/plain; charset=utf-8' });
            fileSaver.saveAs(list, 'destination_list.txt');