Now I'm working on website authentication, and I have added access and refresh tokens. I want to create an auto-refresh action to obtain a new access token. So, if my server returns an "access denied" error, I want to perform a refresh and retry the previous action using RxJS and Redux Observable.
I success to make refresh request when i got this error (i was following these guides #1, #2). However, I'm still unable to figure out how to call the main action again. I want the actions to be executed in the order of main action, refresh, main action (again), but currently, only two out of the three actions are being executed.
Here is my code, so handleError function is called in catchError to request new access token if needed
const handleError = (action$: Observable<any>, err: any, caught: Observable<any>) => {
if (err.response?.errors?.[0]?.extensions?.code === "ACCESS_DENIED") {
return action$.pipe(
ofType(refreshSuccessActionCreator),
takeUntil(action$.pipe(ofType(LOGOUT_ACTION))),
take(1),
mergeMap(() => caught), //it is supposed to try main action again, but it doesn't
mergeWith(of(refreshActionCreator())), //calling refresh
);
}
else {
return of(workSessionErrorActionCreator(err));
}
};
//my epic
export const GetActiveWorkSessionEpic: Epic = (action$: Observable<PayloadAction<string>>) =>
action$.pipe(
ofType(GET_ACTIVE_WORK_SESSION_ACTION),
map(action => action.payload),
mergeMap((userId) => RequestGetActiveWorkSession(userId).pipe(
map(res => res.data?.workSession.getActiveWorkSessionByUserId),
map(workSession => SetActiveWorkSession(workSession)),
catchError((err, caught) => handleError(action$, err, caught)),
))
);
//action creators
export const refreshActionCreator = () => ({type: REFRESH_ACTION});
export const refreshSuccessActionCreator = () => ({type: REFRESH_SUCCESS_ACTION});
//refresh epic
export const RefreshEpic: Epic = (action$) =>
action$.pipe(
ofType(REFRESH_ACTION),
mergeMap(() => RequestRefresh().pipe(
map(res => {
if (res.data.auth.refresh) {
SetAccessToken(res.data.auth.refresh);
}
return refreshSuccessActionCreator();
}),
catchError(() => of(logoutActionCreator()))
))
);
Also, I don't understand how my handleError epics work:
- Why do I have to pass the actionCreator instead of the action type in
ofType()
? When i put action type here it just don't work and infinitely request main action. - Why does
mergeMap()
have no effect here?"
I was trying to change mergeMap
and mergeWith
to concatMap
and concatWith
. Also tryied some more rxjs operators here, but it don't work. Tryied to change placement in pipe()
. I can`t to much as i don't understand this epic, but I have already spent many hours with it.