访问状态或存储效果?

Accessing state or store in effects?

我试图访问 NgrX 效果中的当前 state/store,它仅返回初始状态。 但是当我尝试从组件访问它时,它工作正常。

效果class

     @Injectable()
export class RootEffects {
  /*
  To handle the behaviour of the Effect when different Action instances 
  occurs on the same effect you can change mergeMap to other operators
  */
  constructor(
    private actions$: Actions,
    private mockApi: MockApiService,
    private store: Store<any>
  ) {}



  // effect from simulating an API call success
  getMockDataEffect$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ApiGetMockData),
      tap(() => {
        console.log('');
      }),
      mergeMap((action) => {
        console.log('');
        return this.mockApi.getData().pipe(
          map((res) => ApiSuccess({ data: res })),
          // catchError((error) => of(ApiError({ error }))),
          tap(() => {
            console.log('');
          })
        );
      })
    )
  );




  TestEffect$ = createEffect(() =>
    this.actions$.pipe(
      ofType(TestAction),
      tap(() => {
        console.log('test action in que');
      }),
      // this.store.select(selectAccounts)
      withLatestFrom(this.store),
      // withLatestFrom(this.store.select(getStateSelectedData)),
      mergeMap(([action, store]) => {
        console.log('action', action);
        console.log('store', store);
        console.log('test effect running');
        return this.mockApi
          .postData({
            data: 'this data suppossed to get from store from get sucssess action',
          })
          .pipe(
            map((res) => TestActionSuccess({ data: res })),
            // catchError((error) => of(ApiError({ error }))),
            tap(() => {
              console.log('test effect Finished');
            })
          );
      })
    )
  );
}

正在调度操作:

 getApiData() {
    this.store.dispatch(fromRoot.ApiGetMockData());
    this.store.dispatch(fromRoot.TestAction());
  }

第一个调度操作 (fromRoot.ApiGetMockData()) 触发 getMockDataEffect$,它在内部调用 API 并在成功时调度 ApiSuccess 操作。

第二个调度操作 (fromRoot.TestAction()) 需要来自 ApiSuccess 的数据。 但是因为它是不等待响应的异步调用,所以第二个动作正在被调度。

所以无论如何我们可以等待第一个动作的成功响应然后调度第二个动作。

我的想法:

  1. 最初我想在效果中调度第二个动作 (fromRoot.TestAction()),就在 api 成功响应之后,但仍然没有用
  2. 在 mergemap 之后添加一个 tap 运算符并分派操作,仍然没有用
  3. 我注意到在 effects 中从 store 调度一个动作不是一个好的做法https://github.com/timdeschryver/eslint-plugin-ngrx/blob/main/docs/rules/no-dispatch-in-effects.md

这里是 stackBlitz code

如果第二个动作总是依赖于第一个动作的结果,我认为最好添加一个新的 @Effect 来处理 ApiSuccess 动作,然后 map 它到 return 另一个 TestAction 将您在 TestAction 操作的 @Effect 中需要的数据传递给它。 (这需要更改要从新的 @Effect 传递的 TestAction 操作的有效负载,并且不需要从组件调度 TestAction

您可以尝试以下操作:

// Action
const TEST_ACTION = '[Random] test action';
export const TestAction = createAction(TEST_ACTION, props<{ data: any }>());

// New Effect
apiSuccessEffect$ = createEffect(() =>
  this.actions$.pipe(
    ofType(ApiSuccess),
    map((data) => TestAction(data))
  )
);

// TestAction Effect:
testEffect$ = createEffect(() =>
  this.actions$.pipe(
    ofType(TestAction),
    mergeMap((action) => {
      return this.mockApi.postData({ data: action.data }).pipe(
        map((res) => TestActionSuccess({ data: res })),
        tap(() => {
          console.log('test effect Finished');
        })
      );
    })
  )
);

这是您的 StackBlitz

的工作版本

备选解决方案 (not recommended) 是 return 来自 getMockDataEffect 的两个操作,而不是创建新的 @Effect,如下所示:

getMockDataEffect$ = createEffect(() =>
  this.actions$.pipe(
    ofType(ApiGetMockData),
    tap(() => {
      console.log('');
    }),
    mergeMap((action) => {
      console.log('');
      return this.mockApi.getData().pipe(
        switchMap((res) => [
          ApiSuccess({ data: res }),
          TestAction({ data: res }),
        ]),
        tap(() => {
          console.log('');
        })
      );
    })
  )
);

你遇到的问题是,一个接一个地派遣两者只会导致它们尽快运行,前者不等待后者。这实际上是一种良好且需要的行为,因为我们不想阻止执行 - 它是异步的。如果我们想对效果的结果采取行动,我们可以通过在异步调用完成效果后分派一个动作来实现。

so is there anyway we can wait for the success response from first action and then dispatch the second action.

这可能是可行的,但会很麻烦,您不应该这样做。

只需在 getMockDataEffect

的返回操作上连接 TestAction 调用
TestEffect$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ApiSuccess),
      mergeMap(({data}) => {
        return this.mockApi
          .postData({ data })
          .pipe(
            map((res) => TestActionSuccess({ data: res })),
            // catchError((error) => of(ApiError({ error }))),
          );
      })
    )
  );
}

avoid-dispatching-multiple-actions-sequentially