Search code examples
javascriptreactjsreact-reduxredux-toolkitrxjs-observables

How can we dispatch action from an action


I have the following epic file which is having following function,

I need to call action from onUndo but as seen the action.showwarnToast is already dispatch because of which another action is not dispatching

export const makeXDiningReservation = createDataEpic({
  name: "makeXDiningReservation",
  apiPath: (action, state) =>
    generatePath("pathToApi", {
      
    }),
  apiOptions: (action, state) => {
    return {
      headers: {
        "Client-Id": "ship",
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    };
  },
  successActionCreator: (
    response,
    state,
    action,
    { getIntl, ...rest },
    ...params
  ) => {
   

   if (isDailyPlannerEnabled && eventOverlap) {
      return [
        Actions.throttledScheduleRefresh({ force: true }),
        Actions.diningReservationSuccessOnShip({
          ...action,
          reservationID: response.bookingId,
        }),
        Actions.navigateReplace({
          pathname: generatePath("path", {
            restaurantKey: action.restaurantKey,
          }),
          state: {
            action,
            response: { ...response, reservationID: response.bookingId },
          },
        }),
        Actions.showWarnToastMessage(
          getIntl(state).formatMessage(
            strings.activityOverlapsWithAnotherActivity,
          ),
          {
            color: "warning",
            variant: "standard",
            showUndo: true,
            undoData: response,
            onUndo: (data) => {
              return [
                Actions.navigatePush({
                  pathname: generatePath("/dailyPlanner"),
                }),
                modalActivityActionDrawerActions.setModalState(true),
              ];
            },
          },
        ),
      ];
    } else {
     return [
      Actions.diningReservationSuccessOnShip({
          ...action,
          reservationID: response.bookingId,
      }),
      Actions.navigateReplace({
          pathname: generatePath("/dining/:restaurantKey/confirm", {
            restaurantKey: action.restaurantKey,
          }),
          state: {
            action,
            response: { ...response, reservationID: response.bookingId },
          },
        }),
      ];
    }
  },
  failureActionCreator: (error, state, action) => [
    Actions.epicError(makeXDiningReservation.name, error),
    stopSubmit(action.form, { _error: error }),
  ],
  triggeringActions: [START_DINING_RESERVATION],
  type: REQUEST_TYPE.POST_JSON,
  waitingIdentifier: "DINING_RESERVATION",
});

I tried to dispatch like this but it did not work help me to fix it as i am not sure how this will work, eventually i want this to be done from epic side.

onUndo: (data) => {
  return [
    Actions.navigatePush({ pathname: generatePath("/dailyPlanner") }),
    modalActivityActionDrawerActions.setModalState(true),
  ];
};

Solution

  • The epics don't have free access to the store's dispatch function since that is what the successActionCreator and failureActionCreator handlers do under the hood, but what I'd suggest is to create a thunk that does. The new Thunk would dispatch the showWarnToastMessage action to initiate the toast, and can wrap the undo actions in the thunkApi.dispatch call.

    Example:

    import { createAsyncThunk } from '@reduxjs/toolkit';
    
    export const warnUndoToast = createAsyncThunk(
      "toasts/warnUndoToast",
      ({ message, undoData, undoActions = [] }, { dispatch }) => {
        dispatch(Actions.showWarnToastMessage(
          message,
          {
            color: "warning",
            variant: "standard",
            showUndo: true,
            undoData,
            onUndo: (data) => {
              undoActions.forEach(dispatch);
            },
          },
        ));
      }
    );
    

    Now instead of dispatching the Actions.showWarnToastMessage action directly you will dispatch this new toastWarnUndo action in the epic, passing the appropriate props.

    export const makeXDiningReservation = createDataEpic({
      ....
      successActionCreator: (
        response,
        state,
        action,
        { getIntl, ...rest },
        ...params
      ) => {
        ...
    
        if (isDailyPlannerEnabled && eventOverlap) {
          return [
            ...,
            warnUndoToast({
              message: getIntl(state).formatMessage(
                strings.activityOverlapsWithAnotherActivity,
              ),
              undoData: response,
              undoActions: [
                Actions.navigatePush("/dailyPlanner"),
                modalActivityActionDrawerActions.setModalState(true),
              ],
            }),
          ];
        } else {
          return [
            ...
          ];
        }
      },
      ...,
    });