reactjstypescriptrxjsredux-observabletypesafe-actions

Usage of epics with typesafe-actions and Connected React Router


I am currently setting up an epic that listens to the action of type LOCATION_CHANGE or LocationChangeAction, which is an action that is triggered whenever the router history changes due to router actions such as push or replace.

import { LOCATION_CHANGE, LocationChangeAction } from 'connected-react-router';
import { isActionOf } from 'typesafe-actions';

const locationEpic: Epic = (action$) => action$.pipe(
  filter(isActionOf(LocationChangeAction)),
  tap((action) => {
    // do the rest here

  })
);

However, doing the above will throw the error, and adding typeof does not seem to help either.

'LocationChangeAction' only refers to a type, but is being used as a value here.

May I know what is the proper way of doing it, by using typesafe-actions 's isActionOf() operator?


Solution

  • Referring to

    Argument contains array with invalid element at index 0, it should be an action-creator instance from "typesafe-actions"

    It might throw that error because ActionCreator contains ActionCreatorTypeMetadata which requires getType?: () => TType:

    type TypeConstant = string;
    
    export interface ActionCreatorTypeMetadata<TType extends TypeConstant> {
      getType?: () => TType;
    }
    
    ActionCreator
    export type ActionCreator<T extends { type: string }> = ((
      ...args: any[]
    ) => T) &
      ActionCreatorTypeMetadata<T['type']>;
    

    But, the onLocationChanged function implements only the first part of the intersection(a function that returns an object that has a property type).

    export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'
    
    export const onLocationChanged = (location, action, isFirstRendering = false) => ({
      type: LOCATION_CHANGE,
      payload: {
        location,
        action,
        isFirstRendering,
      }
    })
    

    The function must also contain a property getType:

    onLocationChanged.getType = () => `YOUR_TYPE`.
    

    TypeScript Playground


    For those who are using typesafe-actions, you will need to register the LOCATION_CHANGE action,

    import { LOCATION_CHANGE, RouterActionType } from 'connected-react-router';
    import { Location } from 'history';
    import { createAction } from 'typesafe-actions';
    
    namespace RouterActions {
    
      export const onLocationChanged = createAction(
        LOCATION_CHANGE,
        (action) => (location: Location, routerAction: RouterActionType, isFirstRendering?: boolean) => action({
          location,
          action: routerAction,
          isFirstRendering,
        }),
      );
    }
    
    export default RouterActions;
    

    And on your epics, you can simply listen to the LOCATION_CHANGE action,

    const locationEpic: Epic = (action$) => action$.pipe(
      filter(isActionOf(RouterActions.onLocationChanged)),
      switchMap((epic) => {
        // do the rest
      }),
    
    );