redux-toolbelt icon indicating copy to clipboard operation
redux-toolbelt copied to clipboard

re-fire the same asyncAction that failed

Open NirLud opened this issue 7 years ago • 4 comments

Hi, First of all, Thank you for this great package!

Now, My problem is: I need to re-fire the same asyncAction that failed (with the same payload, after changing the local storage accessToken value). How can i do it with out adding it as a metaData to each action?

*userFireAccessToken is just a selector for the refreshed token.

  export const onUnauthorizedResponse = (action$, state$) =>
  action$.pipe(
    switchMap(({type, payload}) => {
      if (type.endsWith('ASYNC_FAILURE') && payload?.response?.status === 401) {
        if(userFireAccessToken(state$.value) && localStorage.getItem('accessToken') !== userFireAccessToken(state$.value)){
          localStorage.setItem('accessToken', userFireAccessToken(state$.value));
          return [
            /// HERE I WANT TO REFIRE THE LAST ACTION (THE ONE THAT FAILD) WITH THE SAME PAYLOAD 
          ];
        }
      }
      return [];
    }),
  );

NirLud avatar Nov 10 '18 10:11 NirLud

Word of advice - wrap you code example in triple backticks

```javascript your code here ```

gioragutt avatar Nov 10 '18 12:11 gioragutt

Will this work?

export const onUnauthorizedResponse = (action$, state$) =>
  action$.pipe(
    switchMap(action => {
      // don't destruct immediately, get the full action, and return it later?
      const {type, payload} = action
      if (type.endsWith('ASYNC_FAILURE') && payload?.response?.status === 401) {
        if(userFireAccessToken(state$.value) && localStorage.getItem('accessToken') !== userFireAccessToken(state$.value)){
          localStorage.setItem('accessToken', userFireAccessToken(state$.value));
          return [action];
        }
      }
      return [];
    }),
  );

gioragutt avatar Nov 10 '18 13:11 gioragutt

No... you are trying to fire an action of type ASYNC_FAILURE with its payload. I want to fire the action how fires this failure, The ASYNC_REQUEST.

NirLud avatar Nov 10 '18 14:11 NirLud

Here is my solution for this problem. Hope it will be helpful for some other visitors from the future.

Epic

  export const onSaveLastAsyncRequest = (action$) =>
  action$.pipe(
    switchMap((action) => {
      if (action.type.endsWith('ASYNC_REQUEST')) {
          return [saveLastAsyncRequest({actionToFire: action})];
        }
        return [];
      }),
  );

Epic

  export const onUnauthorizedResponse = (action$, state$) =>
  action$.pipe(
    tap(async ({type, payload}) => {
      if (type.endsWith('ASYNC_FAILURE') && payload?.response?.status === 401) {
        const user = await firebase.auth().currentUser;
        const userToken = await user?.getIdToken();
        await localStorage.setItem('accessToken', userToken || accessToken());
      }
  }),
    switchMap(({type, payload}) => {
      if (type.endsWith('ASYNC_FAILURE') && payload?.response?.status === 401) {        
          return [state$.value.lastAsyncRequest?.actionToFire];
      }
      return [];
    }),
  );

Action

export const saveLastAsyncRequest = makeActionCreator('SAVE_LAST_ASYNC_REQUEST_TO_STATE');

Reducer

export const lastAsyncRequest = (state= null, action)=> {
  switch (action.type) {
    case actions.saveLastAsyncRequest.TYPE:
      return {
        ...action.payload, 
      };
    default:
      return state;
  }
};

NirLud avatar Nov 10 '18 21:11 NirLud