Поток и маршрутизация NGRX - компонент обращался слишком много раз - PullRequest
3 голосов
/ 25 июня 2019

После того, как я внедрил хранилища NGRX в своем приложении, я обнаружил, что мой HomeComponent загружается слишком много раз.

Поток, как показано ниже, с самого начала:

1 - При вызове страницы пытается загрузить панель мониторинга, но AuthGuard сообщает, что пользователь не вошел в систему, и загружает компонент LoginComponent.

приложение-routing.module.ts

const routes: Routes = [
  {
    path: 'login',
    loadChildren: './landing/landing.module#LandingModule'
  },
  {
    path: '',
    canActivate: [AuthGuard],
    loadChildren: './dashboard/dashboard.module#DashboardModule'
  }
];

2 - Затем пользователь выбирает для входа через Facebook.

login.component.ts

signInWithFacebook() {
  this.store.dispatch(new FacebookLogIn());
}

3 - редуктор вызывается, вызовите мой LoginService и, если аутентификация в порядке, отправьте эффект LogInSuccess. Чтобы возобновить, я не буду размещать эту часть.

4 - Если вход в систему выполнен успешно, мне нужно загрузить другую информацию о пользователе, поэтому я звоню в другие магазины и только затем перехожу к моему DashboardComponent.

@Effect({ dispatch: false })
LogInSuccess: Observable<any> = this.actions.pipe(
  ofType(LoginActionTypes.LOGIN_SUCCESS),
  tap(action => {
    this.zone.run(() => {
      this.store.dispatch(new GetData(action.payload.user.email));
      this.store.dispatch(new GetData2(action.payload.user.uid));
      localStorage.setItem('user', JSON.stringify(action.payload.user));
      this.router.navigate(['/dashboard'])
    });
  })
);

5 - Панель инструментов загружает HomeComponent вместе.

приборная панель-routing.module.ts

{
  path: 'dashboard',
  component: DashboardComponent,
  canActivate: [AuthGuard],
  children: [
    {
      path: '',
      component: HomeComponent,
    },
    ...
    ...
  ]
}

6 - звонки магазина приводят к следующему:

enter image description here

7 - И здесь проблема. Если я создаю файл console.log в HomeComponent, я вижу, что он вызывается 1 раз для каждого магазина, как показано ниже.

enter image description here

Вопросы:

Почему?

Что я должен сделать, чтобы предотвратить все эти ненужные нагрузки?

Если я удаляю одну из вышеприведенных рассылок, она отправляется HomeComponent только 3 раза, а не 5, как на рисунке, потому что она удаляет 2 эффекта.

- Обновление -

HomeComponent.ts

isTermSigned = false;
homeInfo = {
  isBeta: false,
  isTermSigned: false,
  displayName: '',
  email: ''
};
homeSubscription: Subscription;

constructor(
  private afs: AngularFirestore,
  private router: Router,
  private store: Store<AppState>
) { }

ngOnInit() {
  this.homeSubscription = combineLatest(
    this.store.pipe(select(selectData)),
    this.store.pipe(select(selectStatusLogin))
  ).subscribe(([data, login]) => {
    console.log(login);
    if (login.user) {
      this.homeInfo = {
        isBeta: data.isBeta,
        isTermSigned: data.isBeta,
        displayName: login.user.displayName,
        email: login.user.email
      };
    }
  });
}

- Обновление 2 - Вот важная часть хранилища данных

data.action.ts

export class GetData implements Action {
  readonly type = PlayerActionTypes.GET_BETA_USER;
  constructor(public payload: any) {}
}

export class GetDataSuccess implements Action {
  readonly type = PlayerActionTypes.GET_DATA_SUCCESS;
  constructor(public payload: any) {}
}

data.effect.ts

@Effect()
GetData: Observable<any> = this.actions.pipe(
  ofType(PlayerActionTypes.GET_DATA),
  mergeMap(email =>
    this.dataService
      .getData(email)
      .then(data=> {
        return new GetDataSuccess({
          isBeta: data.email ? true : false,
          isTermSigned: data.acceptTerms ? true : false
        });
      })
      .catch(error => {
        return new GetDataError({
          isBetaUser: false,
          isTermSigned: false
        });
      })
  )
);

@Effect({ dispatch: false })
GetDataSuccess: Observable<any> = this.actions.pipe(
  ofType(PlayerActionTypes.GET_DATA_SUCCESS),
  tap(action => {
    localStorage.setItem('data', JSON.stringify(action.payload));
  })
);

data.reducer.ts

export interface State {
  isBeta: boolean;
  isTermSigned: boolean;
}

export const initialState: State = {
  isBeta: false,
  isTermSigned: false
};

export function reducer(state = initialState, action: All): State {
  switch (action.type) {
    case DataActionTypes.GET_DATA_SUCCESS: {
      return {
        ...state,
        isBeta: action.payload.isBeta,
        isTermSigned: action.payload.isTermSigned
      };
    }
    case DataActionTypes.GET_DATA_ERROR: {
      return {
        ...state,
        isBeta: action.payload.isBeta,
        isTermSigned: action.payload.isTermSigned
      };
    }
    ...
    default: {
      const data = JSON.parse(localStorage.getItem('data'));
      if (data) {
        return {
          ...state,
          isBeta: betaUser.isBeta,
          isTermSigned: betaUser.isTermSigned
        };
      } else {
        return state;
      }
    }
  }
}

data.selector.ts

import { AppState } from '../reducers';

export const selectData = (state: AppState) => state.data;

- Обновление 3 -

Еще одна вещь, которая может помочь и разрушает мой разум, когда я выхожу из системы, вызывается один и только один эффект, но мой HomeComponent, который вообще не имеет перенаправления, вызывается дважды:

{isAuthenticated: true, user: {…}, errorMessage: null}
{isAuthenticated: false, user: null, errorMessage: null}

1 Ответ

4 голосов
/ 28 июня 2019

Я не уверен, что полностью понимаю ваш контекст и ваши потребности, но я думаю, что ваш HomeComponent не загружается несколько раз. Однако наблюдаемая, созданная с помощью combineLatest, получает несколько раз одно и то же значение.

Могу ли я предложить вам 2 возможных улучшения:

1) Использование селекторов для составления нескольких срезов хранилища.

Например, вы можете создать селектор getHomeInfo, чтобы получать всю необходимую вам информацию, и избегать вызова combineLatest внутри HomeComponent. Это чище, лучше задокументировано, а также лучше для следующего пункта.

2) Используйте запомненные селекторы с createSelector

Проверьте это хороший пост от Тодда Девиз.

Мемоизированные селекторы позволят избежать бесполезных вычислений, а также бесполезных значений, выделяемых в наблюдаемых Вы получите уведомление только в случае обновления значения.

Пример

Чтобы проиллюстрировать эти 2 пункта, я создал проект на stackblitz: https://stackblitz.com/edit/angular-ajhyz4

Без createSelector:

export const getValueWithoutCreateSelector = (state) => {
  return state.app.value;
};

С createSelector:

export const getValue = createSelector(getAppState, (state) => {
  return state.value;
});

Составной селектор:

export const getCustomMessage = createSelector(getValue, getText,
  (value, text) => {
    return `Your value is ${value} and text is '${text}'`;
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...