使用Redux Toolkit和Typescript反应可观察的史诗

问题描述

我不确定如何使用Redux Toolkit和Typescript编写React可观察的史诗。

假设我有这个authSlice:

import { CaseReducer,createSlice,PayloadAction } from "@reduxjs/toolkit";

type AuthState = {
  token: string,loading: boolean,};

const initialState: AuthState = {
  token: "",loading: false,};

const loginStart: CaseReducer<AuthState,PayloadAction<{username: string,password: string}>> = (state,action) => ({
  ...state,loading: true,token: "",});

const loginCompleted: CaseReducer<AuthState,PayloadAction<{token: string}>> = (state,token: action.payload.token,});

const authSlice = createSlice({
  name: 'auth',initialState,reducers: {
    loginStart,loginCompleted,},});

export default authSlice;

和这家商店:

import { configureStore } from '@reduxjs/toolkit';
import { combineEpics,createEpicMiddleware } from 'redux-observable';
import authEpic from './epics/authEpic';
import authSlice from './slices/authSlice';

const epicMiddleware = createEpicMiddleware();

export const rootEpic = combineEpics(
  authEpic
);

const store = configureStore({
  reducer: {
    auth: authSlice.reducer,middleware: [epicMiddleware]
});

epicMiddleware.run(rootEpic);

export type RootState = ReturnType<typeof store.getState>;
export default store;

我应该怎么写这个authEpic(我希望目的是不言自明的):

import { Action,Observable } from 'redux';
import { ActionsObservable,ofType } from 'redux-observable';
import { ajax } from 'rxjs/ajax';
import { switchMap } from 'rxjs/operators';
import authSlice from '../slices/authSlice';

export default (action$: ActionsObservable<???>) => action$.pipe(
  ofType(???),/* should be of type loginStart */
  switchMap<???,???>(action => ajax.post( // should be from a loginStart action to {token: string}
    "url",{
      username: action.payload.username,password: action.payload.password 
    }
  )),...
);

我对???完全感到困惑。那应该是什么类型,以及应该如何将redux的可观察性与redux工具包链接

有任何提示吗?

解决方法

在redux-toolkit中,您应该在action.match中使用filter函数而不是ofType来进行类似stated in the documentation的工作流程。

无论使用createActioncreateSlice还是createAsyncThunk创建文档,此文档中的示例都适用于所有RTK操作。

import { createAction,Action } from '@reduxjs/toolkit'
import { Observable } from 'rxjs'
import { map,filter } from 'rxjs/operators'

const increment = createAction<number>('INCREMENT')

export const epic = (actions$: Observable<Action>) =>
  actions$.pipe(
    filter(increment.match),map((action) => {
      // action.payload can be safely used as number here (and will also be correctly inferred by TypeScript)
      // ...
    })
  )
,

问题在于redux-toolkit会使操作模糊,因此很难知道操作类型是什么。在传统的redux设置中,它们只是一堆常量。

type T = ReturnType<typeof authSlice.actions.loginStart>['type']; // T is string

// have to create an action to find the actual value of the string
const action = authSlice.actions.loginStart({username: "name",password: "pw"});
const type = action.type;
console.log(type);

action.type创建的操作的authSlice.actions.loginStart似乎是“ auth / loginStart”,其类型仅为string而不是特定的字符串文字。公式为${sliceName}/${reducerName}。因此ofType变为

ofType("auth/loginStart")

现在是通用注释。我们的authEpic正在执行登录开始操作,并将其转换为登录完成操作。我们可以通过查看authSlice来大致了解这两种类型:

type LoginStartAction = ReturnType<typeof authSlice.actions.loginStart>`)

但这很愚蠢,因为我们从创建authSlice时就已经知道动作类型。操作类型是PayloadAction内部的CaseReducer。让我们别名并导出它们:

export type LoginStartAction = PayloadAction<{ username: string; password: string }>;

export type LoginCompletedAction = PayloadAction<{ token: string }>;

这些是用于减少大小写的类型:

const loginStart: CaseReducer<AuthState,LoginStartAction> = ...

const loginCompleted: CaseReducer<AuthState,LoginCompletedAction> = ...

我对可观察性和史诗不太熟悉,但是我认为您希望在authEpic上键入以下内容:

export default (action$: ActionsObservable<LoginStartAction>) => action$.pipe(
    ofType("auth/loginStart"),switchMap<LoginStartAction,ObservableInput<LoginCompletedAction>>(
        action => ajax.post(
            "url",action.payload
        )
    )
    ...
);
,

我通读了 redux-toolkit 文档,并尽可能将其应用到 redux-observable 中。这是我想出来的。

Root(recognized by git)>
  ExpoProject(tracked by git but wont be commited)>
  README.txt