Ngrx/ данные, по-видимому, мешают восстановителям

Я довольно новичок в Ngrx, поэтому я предполагаю, что я делаю что-то не так. Похоже, что если Ngrx/data активен, он вмешивается в историю состояний, что приводит к вызову редукторов с неопределенными начальными состояниями.

Это очевидно, если у меня есть несколько состояний в корне. Редуктор для одного состояния влияет на другое состояние.

Я создал небольшое приложение, чтобы продемонстрировать проблему. Я добавил EntityDataModule в мой файл модуля следующим образом:

  imports: [
    BrowserModule,
    HttpClientModule,
    EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
    BrowserAnimationsModule,
    StoreModule.forRoot(reducers),
    EffectsModule.forRoot([ EffectService ]),
    StoreDevtoolsModule.instrument(),
  ],

Вот мой код магазина:

import { ActionReducerMap, createAction, createReducer, createSelector, on, props } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';

export interface FirstState {
  value: string;
}

export interface SecondState {
  value: string;
}

export interface State {
  first: FirstState;
  second: SecondState;
}

const initialFirstState: FirstState = {
  value: 'firstValue'
};

const initialSecondState: SecondState = {
  value: 'secondValue'
};

export const selectFirstState = (state: State) => state.first;
export const selectSecondState = (state: State) => state.second;

export const selectFirstValue = createSelector(selectFirstState, (state: FirstState) => state.value);
export const selectSecondValue = createSelector(selectSecondState, (state: SecondState) => state.value);

export const indirectAction = createAction(
  '[Indirect] Use Effects',
);

export const firstValueSet = createAction(
  '[First] Set Value',
  props<{ theValue: string }>()
);

export const secondValueSet = createAction(
  '[Second] Set Value',
  props<{ theValue: string }>()
);

export const firstReducer = createReducer(
  initialFirstState,
  on(firstValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);

export const secondReducer = createReducer(
  initialSecondState,
  on(secondValueSet, (state, { theValue }) => ({ ...state, value: theValue }))
);

export const reducers: ActionReducerMap<State> = {
  first: firstReducer,
  second: secondReducer
};

@Injectable()
export class EffectService {
  constructor(private actions$: Actions) {}

  indirect$ = createEffect(() => this.actions$.pipe(
    ofType(indirectAction),
    map(action => secondValueSet({ theValue: 'INDIRECT' }))
  ));
}

Я создал приложение Stackblitz здесь: https://stackblitz.com/edit/angular-bttdek

Если вы нажмете любую кнопку, которая просто отправляет действия:

  setFirstValue() {
    this.store.dispatch(firstValueSet({ theValue: 'S1:' + Math.random() }));
  }

  setSecondValue() {
    this.store.dispatch(secondValueSet({ theValue: 'S2:' + Math.random() }));
  }

  indirect() {
    this.store.dispatch(indirectAction());
  }

Вы увидите, что другое состояние сбрасывается в исходное состояние. Если я удаляю EntityDataModule.forRoot() из модуля, он работает правильно. Таким образом, кажется, что наличие активного EntityDataModule вызывает проблемы в магазине. Я прошел по магазину и обнаружил, что индекс до последнего нефиксированного состояния отключен, что приводит к вызову редукторов с неопределенным начальным состоянием.

С активным EntityDataModule

Если я удаляю EntityDataModule следующим образом:

  imports: [
    BrowserModule,
    HttpClientModule,
    BrowserAnimationsModule,
    StoreModule.forRoot(reducers),
    EffectsModule.forRoot([ EffectService ]),
    StoreDevtoolsModule.instrument(),
  ],

Работает: с отключенным EntityDataModule

Я что-то неправильно настраиваю?

1 ответ

Решение

Модуль магазина должен быть загружен первым, и это должно решить вашу проблему:

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    BrowserAnimationsModule,
    // ---
    StoreModule.forRoot(reducers),
    EntityDataModule.forRoot({ entityMetadata: { Hero: {} }, pluralNames: { Hero: 'Heroes' }}),
    // ---
    EffectsModule.forRoot([ EffectService ]),
    StoreDevtoolsModule.instrument(),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Другие вопросы по тегам