I was trying to access the current state/store in NgrX effect, it is giving back only initial state . but when i try to access it from component , it is working fine.
Effects 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');
})
);
})
)
);
}
dispatching the actions:
getApiData() {
this.store.dispatch(fromRoot.ApiGetMockData());
this.store.dispatch(fromRoot.TestAction());
}
First dispatched action (fromRoot.ApiGetMockData()) triggers getMockDataEffect$
which internally calls an API and on success it dispatches ApiSuccess
action.
Second Dispatched action(fromRoot.TestAction()) needs the data from ApiSuccess
.
but since it is async call without waiting for the response , the second action is getting dispatched.
so is there anyway we can wait for the success response from first action and then dispatch the second action.
my thoughts:
tap
operator after the mergemap and dispatch the action , still did not workhere is the stackBlitz code
If the second action always depends on the first one's result, I think it's better to add a new @Effect
to handle the ApiSuccess
action, and then map
it to return the other TestAction
passing to it the data that you need in the @Effect
of the TestAction
action. (This requires changing the payload of the TestAction
action to be passed from the new @Effect
, and there is no need to dispatch the TestAction
from the component).
You can try something like the following:
// 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');
})
);
})
)
);
And here is a working version of your StackBlitz
The alternative solution (not recommended) is to return both actions from the getMockDataEffect
, instead of creating a new @Effect
, like the following:
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('');
})
);
})
)
);