问题描述
我不确定如何使用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的工作流程。
无论使用createAction
,createSlice
还是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