首页 > 解决方案 > 如何防止 Angular 商店调度创建无限循环

问题描述

我最近遇到一个问题,并写了一个简化版本。我想知道解决此类问题的最佳方法是什么。假设您有一个简单的状态,其中包含一个 updateState 操作和一个选择器,它从状态中选择该值,如下所示

const defaults: AppStateModel = {
  test: 'test'
};

@State<AppStateModel>({
  name: 'app',
  defaults
})
@Injectable()
export class AppState {

  @Selector()
  static testSelector(state: AppStateModel): string {
    return state.test;
  }

  @Action(UpdateTest)
  updateState(ctx: StateContext<AppStateModel>, { nextTest }: UpdateTest) {
    ctx.patchState({
      test: nextTest
    });
  }
}

然后我从一个组件中调用一个函数,该函数将来自选择器的异步管道作为输入,然后像这样调度操作。

@Component({
  selector: 'app-root',
  template: `
    <input [(ngModel)]="name">
    <p>Test: {{testFunction(testSelector$ | async)}}</p>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @Select(AppState.testSelector)
  testSelector$: Observable<string>;
  name: string;

  constructor(private store: Store) {
  }

  testFunction(test: string): string {
    console.log("testprint");
    this.store.dispatch(new UpdateTest(this.name));
    return test;
  }
}

现在这将创建一个无限循环,因为状态更新并且组件不断接收更新的状态值,但是您将如何防止这种情况发生呢?如果我真的需要组件使用来自选择器的输入调用函数,但该函数也需要更改相同的状态怎么办?

标签: angulartypescriptreduxngxs

解决方案


您可以将选择器的输出用作操作输入,不用担心。我知道这是一个人为的 SO 示例,但它有点不清楚你何时想要调度动作来改变状态 - 它通常会被驱离一个事件或另一个 Observable - 如果你在模板中有它那么它'将在每个更改检测周期中调用(这肯定不是您想要的)。

通常你想触发一些输入的改变——然后通过一个动作改变状态。

@Component({
  selector: 'app-root',
  template: `
    <input [ngModel]="name" (ngModelChange)="nameChanged($event)>
    <p>Test: {{ testSelector$ | async }}</p>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  @Select(AppState.testSelector) testSelector$: Observable<string>;

  name: string;

  constructor(private store: Store) {
  }

  nameChanged(newName: string): void {
    this.store.dispatch(new UpdateTest(newName));
  }
}

推荐阅读