使用 epics 与 typesafe-actions 和 Connected React Router

Usage of epics with typesafe-actions and Connected React Router

我目前正在设置一个史诗来侦听类型为 LOCATION_CHANGELocationChangeAction 的动作,该动作会在路由器历史记录因路由器动作(例如 pushreplace.

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

  })
);

但是,执行上述操作会引发错误,添加 typeof 似乎也无济于事。

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

我可以知道使用 typesafe-actions 的正确方法是什么吗 的 isActionOf() 运算符?

参考

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

它可能会抛出该错误,因为 ActionCreator 包含需要 getType?: () => TType:

ActionCreatorTypeMetadata
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']>;

但是,onLocationChanged 函数只实现交集的第一部分(returns 一个 object 的函数,它有一个 属性 type ).

export const LOCATION_CHANGE = '@@router/LOCATION_CHANGE'

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

该函数还必须包含一个 属性 getType:

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

TypeScript Playground


使用typesafe-actions的用户需要注册LOCATION_CHANGE动作,

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;

在你的 epics 上,你可以简单地听取 LOCATION_CHANGE 动作,

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

);

除了 Andrei 的出色回答外,我还想为我的问题提出一个替代解决方案。

明确定义 'fake' 动作以反映 connected-react-router (such as push, replace, onLocationChanged) is that, it will case further downstream issues when you need to call/listen to the action on your epics (such as redux-observables 中的历史动作方法的问题。

因此,处理此问题的更好方法是在主 RootAction 上添加 History 操作方法的类型。例如,如果您希望将 replace 操作添加为 Redux RootAction

的一部分
import { CallHistoryMethodAction, connectRouter, RouterState } from 'connected-react-router';
import { LocationState, Path } from 'history';

const actions = {
  userActions,
  settingsActions
};

type Replace = (path: Path, state?: LocationState) => CallHistoryMethodAction<[Path, LocationState?]>;

interface RouteActions {
  replace: Replace;
}

export type RootAction = ActionType<typeof actions> | ActionType<RouteActions>;

export interface RootState {
  router: RouterState;
  user: UserState;
  settings: SettingsState;
}

这将防止 TypeScript/typesafe-actions 标记您的历史操作未定义的错误。