Эффект ngrx не вызывается, когда действие отправляется в конструкторе компонента - PullRequest
1 голос
/ 09 января 2020

Вот мой модуль приложения:

@NgModule({
  declarations: [
    SampleA
  ],
  imports: [
    BrowserModule,
    EffectsModule.forRoot([APIEffects]),
    HttpClientModule,
    StoreModule.forRoot(reducers),
    StoreDevtoolsModule.instrument({
      maxAge: 25, // Retains last 25 states
      logOnly: environment.production, // Restrict extension to log-only mode
    }),
    KeyboardShortcutsModule.forRoot()
  ],
  providers: [
    APIService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpMockRequestInterceptor,
      multi: true
    }
  ],
  bootstrap: [],
  entryComponents: [
    SampleComponent
  ]
})
export class AppModule{...}

Вот APIEffect:

@Injectable()
export class APIEffects {
  fetchData$ = createEffect(() => this.actions$.pipe(
    ofType(Actions.apiFetchData),
    mergeMap((action) => this.apiService.fetchData(...action)
          .pipe(
            map(data => Actions.apiSuccess({ data })),
            catchError(() => of(Actions.apiCallFailed()))
          )
    )
  ));
}

Теперь, если я отправлю действие apiFetchData в конструктор SampleComponent, эффект не будет Не вызывать:

export class SampleComponent {
    constructor(private store: Store<{ state: State }>) {
        store.dispatch(Actions.apiFetchData(...))
    }
}

Если я отправлю то же действие позже в жизненном цикле компонента, то все будет работать нормально, и эффект произойдет.

В Redux DevTools я могу см. действие отправка происходит до effects init. Вот порядок:

@ngrx/store/init
[API] fetch data // this is the fetch data action
@ngrx/effects/init

Поэтому мой вопрос заключается в том, как заставить эффекты инициализировать до создания моего компонента.

ОБНОВЛЕНИЕ

Я не решил проблему упоминалось выше, но сейчас я решил использовать ROOT_EFFECTS_INIT (спасибо @mitschmidt за упоминание этого) и отправить серию действий после того, как effects будет готов для инициализации состояния моего приложения. Вот эффект, который использует ROOT_EFFECTS_INIT:

  init$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ROOT_EFFECTS_INIT),
      map(action => CoverageUIActions.apiFetchData({...}))
    )
  );

1 Ответ

1 голос
/ 10 января 2020

Для этого вам может понадобиться посмотреть события жизненного цикла NGRX, такие как ROOT_EFFECTS_INIT, как описано здесь: https://ngrx.io/guide/effects/lifecycle

Использование токена инъекции APP_INITIALIZER ( хороший учебник здесь ) плюс необходимый сахар syntacti c, вы можете создать службу инициализатора приложения, которая задерживает загрузку вашего приложения до тех пор, пока не будут зарегистрированы эффекты NGRX root (в этом случае ни один компонент не будет инициализирован, а ваш логика c выше внутри конструктора должна работать). В недавнем проекте я пошел по тому же пути:

export class AppInitializer {
  constructor(private readonly actions$: Actions) {}

  initialize() {
    return this.actions$.pipe(ofType(ROOT_EFFECTS_INIT));
  }
}
  return (): Promise<any> => {
    return appInitializer.initialize().toPromise();
  };
}

... и, наконец, добавил провайдера в ваш модуль:

import { appInitializerFactory } from './app-initializer.factory';

export const APP_INITIALIZER_PROVIDER: Provider = {
  provide: APP_INITIALIZER,
  useFactory: appInitializerFactory,
  deps: [AppInitializer],
  multi: true,
};
// your module...
{
...
  providers: [APP_INITIALIZER_PROVIDER]
...
}
...