angular - NgRx:如何在 meta-reducers 中使用服务?

标签 angular ionic-framework dependency-injection ionic4 ngrx

我使用自定义中间件 ( meta-reducer ) 打印我的 ngrx-store每次调度一个 Action 。我直接在 app.module.ts 里面写了我的中间件(我还应该把它放在哪里?):

app.module.ts

// ...imports

// ...

// FIXME:
/**
 * console.log action and state(before action) each time an action is dipatched
 * @param reducer reducer
 */
export function debug(reducer: ActionReducer<AppState, Actions>): ActionReducer<AppState, Actions> {

    const logger = new LoggerService(); ////////////// ERROR \\\\\\\\\\\\\\

    return (state, action) => {

        logger.storeInfo('ACTION', action);
        logger.storeInfo('STATE', state);

        return reducer(state, action);
    };
}

export const metaReducers: MetaReducer<any>[] = [
    debug,
];

@NgModule({
    declarations: [AppComponent, LoggerServiceComponent],
    entryComponents: [],
    imports: [
        // ...
        StoreModule.forRoot(reducers, { metaReducers }),
        StoreRouterConnectingModule.forRoot(), // Connects RouterModule with StoreModule
    ],
    providers: [
       // ...
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

有一个错误,因为我的 LoggerService注入(inject)了一个存储(因为我希望我的所有日​​志都存储在 ngx-store 中。 但我也无法访问存储!。此外,我确信这不是访问服务的单例实例...
  • 我应该把我的元 reducer 放在一个类中,然后访问那个类的函数,但是我该怎么做呢?
  • 是否有访问任何服务的通用方法,例如 SomeClass.getServiceInstance(type) ?
  • 你对如何做有其他想法吗?

  • [编辑 1] - 使用 META_REDUCERS (第一次尝试 - 失败)

    app.module.ts

    import { LoggerService } from './services/logger.service';
    import { AppState, Actions } from './app.state';
    import { StoreModule, MetaReducer, ActionReducer, META_REDUCERS } from '@ngrx/store';
    
    /**
     * Injects a `LoggerService` inside a `MetaReducer`
     * @param logger a service that allows to log and store console.log() messages
     * @returns a `MetaReducer`
     */
    function debugFactory(logger: LoggerService): MetaReducer<AppState> {
        return (reducer: ActionReducer<AppState, Actions>): ActionReducer<AppState, Actions> => {
            return (state, action) => {
    
               logger.storeInfo('ACTION', action);
               logger.storeInfo('STATE', state);
    
               return reducer(state, action);
            };
        };
    }
    
    /**
     * Injects a LoggerService inside the debug `MetaReducer` function
     * @param logger a service that allows to log and store console.log() messages
     * @returns A list of `MetaReducer`
     */
    export function getMetaReducers(logger: LoggerService): MetaReducer<AppState>[] {
        return [debugFactory(logger)];
    }
    
    const reducers = {
        layout: layoutReducer,
        preferences: preferencesReducer,
        router: routerReducer,
        debug: debugReducer,
    };
    
    @NgModule({
        declarations: [AppComponent ],
        entryComponents: [],
        imports: [
            // ...
            StoreModule.forRoot(reducers),
            StoreRouterConnectingModule.forRoot(), // Connects RouterModule with StoreModule
        ],
        providers: [
            // ...
            {
                provide: META_REDUCERS,
                deps: [LoggerService],
                useFactory: getMetaReducers,
                multi: true,
            },
        ],
        bootstrap: [AppComponent],
    })
    export class AppModule {}
    

    根据文档,这应该可以工作,但在运行时出现以下错误:
    TypeError: "fn is not a function"
    

    对应这个函数(在 njrx-store 库中):

    /**
     * @param {...?} functions
     * @return {?}
     */
    function compose(...functions) {
        return (/**
         * @param {?} arg
         * @return {?}
         */
        function (arg) {
            if (functions.length === 0) {
                return arg;
            }
            /** @type {?} */
            const last = functions[functions.length - 1];
            /** @type {?} */
            const rest = functions.slice(0, -1);
            return rest.reduceRight((/**
             * @param {?} composed
             * @param {?} fn
             * @return {?}
             */
            (composed, fn) => {
                 return fn(composed) // <----- HERE
            }), last(arg));
        });
    }
    

    在调试器中,它显示函数数组( ...functions )包含一些函数和一个数组,我怀疑这是 getMetaReducers 方法的结果.我怀疑示例是错误的,或者 compose 的实现存在问题方法。

    如果您在我的代码中看到任何错误的内容,请告诉我。

    [编辑 2] - 使用 USER_PROVIDED_META_REDUCERS如答案中所述(第二次尝试 - 失败)

    已编辑的代码

    // OLD
    
        providers: [
            // ...
            {
                provide: META_REDUCERS,
                deps: [LoggerService],
                useFactory: getMetaReducers,
                multi: true,
            },
        ],
    
    // NEW
    
        providers: [
            // ...
            {
                provide: USER_PROVIDED_META_REDUCERS,
                deps: [LoggerService],
                useFactory: getMetaReducers,
            },
        ],
    

    看来我的LoggerService没有正确传递也没有初始化,因为我现在有这个错误:
    core.js:9110 ERROR TypeError: Cannot read property 'storeInfo' of undefined
        at http://localhost:8102/main.js:636:20
        at http://localhost:8102/vendor.js:109798:20
        at computeNextEntry (http://localhost:8102/vendor.js:108628:21)
        at recomputeStates (http://localhost:8102/vendor.js:108681:15)
        at http://localhost:8102/vendor.js:109029:26
        at ScanSubscriber.StoreDevtools.liftedAction$.pipe.Object.state [as accumulator] (http://localhost:8102/vendor.js:109081:38)
        at ScanSubscriber._tryNext (http://localhost:8102/vendor.js:120261:27)
        at ScanSubscriber._next (http://localhost:8102/vendor.js:120254:25)
        at ScanSubscriber.next (http://localhost:8102/vendor.js:114391:18)
        at WithLatestFromSubscriber._next (http://localhost:8102/vendor.js:122330:34)
    

    如果我评论这些行:

            logger.storeInfo('ACTION', action);
            logger.storeInfo('STATE', state);
    

    不会抛出异常,但我的记录器也不会工作。

    但是至少存储正确地配置了自己,现在的问题只是 LoggerService 没有正确传递或初始化。我想我还是做错了什么

    最佳答案

    您应该尝试使用 META_REDUCERS 注入(inject)元 reducer 。 token 为 documented :

    ( HEADS UP :截至 24.Sept 文档有点误导,工厂方法返回类型应为 MetaReducer 而不是 MetaReducer[]。检查 this example in the codebase)

    export debugFactory(logger: LoggerService): MetaReducer<AppState> {
     return (reducer: ActionReducer<AppState, Actions>): ActionReducer<AppState, Actions> => {
        return (state, action) => {
    
            logger.storeInfo('ACTION', action);
            logger.storeInfo('STATE', state);
    
            return reducer(state, action);
        };
      }
    }
    
    @NgModule({
      providers: [
        {
          provide: META_REDUCERS,
          deps: [LoggerService],
          useFactory: debugFactory,
          multi: true
        },
      ],
    })
    export class AppModule {}
    

    更新:

    Since v8您可以使用 ÙSER_PROVIDED_META_REDUCERS token :
    export function getMetaReducers(logger: LoggerService): MetaReducer<AppState>[] {
     return [debugFactory(logger)];
    }
    
    providers: [
      {
         provide: USER_PROVIDED_META_REDUCERS,
         deps: [LoggerService],
         useFactory: getMetaReducers
      },
    ],
    

    在这种情况下,提供的工厂方法必须返回 MetaReducer[] . “库”和“用户”提供的元缩减器在 this function 中合并为一个集合.

    您很可能会遇到商店和记录器服务之间的循环依赖关系,因为在创建商店期间,Angular 的 IOC 容器将尝试在商店创建完成之前实例化依赖于商店的记录器服务.您可以通过将 Injector 注入(inject)其中并为存储定义 getter 属性“懒惰地”访问记录器服务中的存储来解决此问题,例如:
    private get() store{return this.injector.get(Store);}
    
    ctor(private readonly injector: Injector){}
    

    关于angular - NgRx:如何在 meta-reducers 中使用服务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58066622/

    相关文章:

    Angular 4 Router - Hook 每个路由器更改

    jquery - ngRoute 在页面之间随机切换

    java - 带注解的依赖注入(inject)

    javascript - 组件初始化之前 Angular 加载异步数据

    html - 如何创建下拉列表并绑定(bind)来自服务器的数据

    javascript - react + ionic 5 : Tab navigation problems

    android - ionic/cordova 错误代码 1,构建平台之一时出错

    asp.net - webforms依赖注入(inject)CS7036 : There is no argument given that corresponds to the required formal parameter

    angular - 扩展已注入(inject)服务的类而不在子类中重复注入(inject)器?

    validation - 如何一次只显示一个验证错误