【问题标题】:How to type redux thunk with middleware in typescript如何在打字稿中使用中间件键入 redux thunk
【发布时间】:2021-06-29 09:35:20
【问题描述】:

所以我有一个 SPFx webpart(使用打字稿)并添加了 Redux。现在一切正常,但是需要键入 thunk 函数,我不知道如何键入它们。

所以这是我要调度的操作:

  export const setListByMiddelware:any = (listId:string) => (dispatch, getState) => {
    dispatch(listIdAdded(listId));
    dispatch(spApiCallBegan({
        method: GET_LIST_ITEMS,
        options: {
            listId
        },
        onSuccess: itemsAdded.type
    }));
  }

这是执行 SP 调用的 SharePoint 中间件:

import SP from '../../services/SharePointService';
import * as actions from '../SP_API';

const api = store => next => async action => {
    if(action.type !== actions.spApiCallBegan.type) {
        next(action);
        return;
    }

    const { method, options, onSuccess, onError, onStart } = action.payload;

    if(onStart)  {
        store.dispatch({ type: onStart });
    }

    next(action);

    try {
        const result = await SP[method](options);

        store.dispatch(actions.spApiCallSuccess(result));
        if(onSuccess) {
            store.dispatch({
                type: onSuccess,
                payload: result
            });
        }
    } catch(ex) {
        store.dispatch(actions.spApiCallFailed(ex.message));
        if(onError) {
            store.dispatch({ 
                type: onError,  
                payload: ex.message 
            });
        }
    }
}

export default api;

现在你可以看到我通过简单地将 thunk 输入到any 解决了这个问题,并且效果很好,但我想强烈输入它,因为这些是我的组件的功能实际使用。

这是我正在使用的堆栈:
redux@4.0.5
react-redux@7.2.3
@reduxjs/toolkit@1.5.1

我在输入 Redux 时遇到了真正的麻烦,正如你所看到的,我或多或少地放弃了这一点,而谷歌在这方面并没有多大帮助。因此,我的 Redux 代码大部分是无类型的。

至于打字稿,我想我使用的是 TS 3.3 自带的 SPFx 1.11.0,我使用的是 SPFx 版本。

编辑
正如 markerikson 所指出的那样,我已经阅读了有关在此处输入 Redux 的官方 Redux 文档: https://redux.js.org/recipes/usage-with-typescript#type-checking-middleware

但是当我实现它时,我得到:Type '(listId: string) => (dispatch: any, getState: any) => void' is notassignable to type 'ThunkAction'。 参数“listId”和“dispatch”的类型不兼容。 类型 'ThunkDispatch' 不可分配给类型 'string'。 对于 thunk'api' 在其自己的类型注释中直接或间接引用. 用于中间件。

这是我正在使用的商店设置:

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import listRedurcer, { IListSate} from './lists';
import SP_API from './middleware/SP_API';


const store = configureStore({
    reducer: listRedurcer,
    middleware: [
        ...getDefaultMiddleware(),
        SP_API
    ],
    devTools : {
        name: 'ReduxList'
    }
});

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;

export default store;

对于 thunk,我也使用了这个 ThunkAction&lt;void, RootState, string, AnyAction&gt;,但我得到了同样的错误。

【问题讨论】:

    标签: typescript redux


    【解决方案1】:

    Redux docs "Usage with TypeScript" page specifically covers typing middleware and thunks:

    自定义中间件如下所示:

    import { Middleware } from 'redux'
    
    import { RootState } from '../store'
    
    export const exampleMiddleware: Middleware<
      {}, // Most middleware do not modify the dispatch return value
      RootState
    > = storeApi => next => action => {
      const state = storeApi.getState() // correctly typed as RootState
    }
    

    对于重击:

    import { AnyAction } from 'redux'
    import { sendMessage } from './store/chat/actions'
    import { RootState } from './store'
    import { ThunkAction } from 'redux-thunk'
    
    export const thunkSendMessage = (
      message: string
    ): ThunkAction<void, RootState, unknown, AnyAction> => async dispatch => {
      const asyncResp = await exampleAPI()
      dispatch(
        sendMessage({
          message,
          user: asyncResp,
          timestamp: new Date().getTime()
        })
      )
    }
    
    function exampleAPI() {
      return Promise.resolve('Async Chat Bot')
    }
    

    更新

    听起来你想像这样使用ThunkAction

    export const setListByMiddelware : ThunkAction = (listId:string) => (dispatch, getState) => {}
    

    这是不正确的。 setListByMiddelware 不是 ThunkAction - 它返回 ThunkAction。正确的用法是:

    export const setListByMiddelware = (listId:string) : ThunkAction => (dispatch, getState) => {}
    

    另外,你的 middleware 参数是错误的:

    • middleware 参数应该是一个接受 getDefaultMiddleware 作为其参数的回调
    • 然后您应该调用它,并使用数组的 prependconcat 方法来设置正确的类型:
    const store = configureStore({
        reducer: listRedurcer,
        middleware: getDefaultMiddleware => getDefaultMiddleware().concat(SP_API),
        devTools : {
            name: 'ReduxList'
        }
    });
    

    https://redux-toolkit.js.org/usage/usage-with-typescript#correct-typings-for-the-dispatch-type

    【讨论】:

    • 所以我确实阅读了文档,也许我应该澄清一下。无论如何,当我使用 ThunkAction&lt;void, RootState, unknown, AnyAction&gt; 类型时,我会收到以下错误 Type '(listId: string) => (dispatch: any, getState: any) => void' is not assignable to type 'ThunkAction'。参数“listId”和“dispatch”的类型不兼容。类型 'ThunkDispatch' 不能分配给类型 'string'。 对不起,我只是不明白,我什至使用了 ThunkAction&lt;void, RootState, string, AnyAction&gt;
    • 显示你在哪里使用ThunkAction类型?
    • 是的,我尝试使用它,但我想我使用的是错误的,当我将它应用到setListByMiddelware thunk 时,我只是得到了上面的错误。
    • 问题似乎是他们不能使用RootState 来键入中间件,同时还从store 派生RootState 类型,因为这是循环的(因为store 取决于中间件)。我建议明确输入RootState。你有更好的主意吗?
    • 是的,在这种情况下,解决方法是编写一个单独的rootReducer 函数,然后执行type RootState = ReturnType&lt;typeof rootReducer&gt;,而不是来自store.getState
    【解决方案2】:

    ThunkAction&lt;void, RootState, string, AnyAction&gt; 如果你一次性创建了你的 thunk 是合适的,比如setListByMiddelware=(dispatch, getState, listId:string)=&gt;{},但setListByMiddelware 本身不是一个 thunk,它更像是一个 thunk 工厂,所以你需要它的类型来反映它,试试

    const setListByMiddelware:(listId:string)=>ThunkAction<void, RootState, never, AnyAction> = 
      (listId:string) => (dispatch, getState) => {
    

    【讨论】:

    • 当我尝试您的解决方案时,我得到了 “ThunkAction”类型的参数不能分配给“AnyAction”类型的参数。 “ThunkAction”类型中缺少属性“类型”,但在“AnyAction”类型中是必需的。
    • 看起来dispatch输入不正确,试试dispatch:Dispatch
    • 同样的问题,这是我尝试过的实现:export const setListByMiddelware:(listId:string)=&gt;ThunkAction&lt;void, RootState, never, AnyAction&gt; = (listId:string) =&gt; (dispatch:Dispatch, getState) =&gt; { /* thunk code */}
    • 和错误一模一样?..试试dispatch:ThunkDispatch&lt;RootState,never, AnyAction&gt;也许?
    • 是的,有效,但很抱歉我不知道接受谁的答案,因为我从所有提交的答案中得到了答案
    【解决方案3】:

    'api'在自己的类型注解中被直接或间接引用。

    您收到此错误是因为中间件类型取决于派生自storeRootState 类型。但是store 类型取决于中间件,所以你有一个循环类型。

    解决此问题的一种方法是定义RootState 的类型,而不使用store。在你的情况下,你的整个减速器是listReducer,所以你可以这样做:

    export type RootState = ReturnType<typeof listReducer>;
    

    这将适用于@markerikson 建议的api 类型

    const api: Middleware<{}, RootState> = store => next => async action => {
    

    setListByMiddleware 是一个返回类型ThunkAction 的函数。函数本身不是ThunkAction

    export const setListByMiddelware = (
      listId: string
    ): ThunkAction<void, RootState, never, AnyAction> => (dispatch, getState) => {
      dispatch(listIdAdded(listId));
      dispatch(
        spApiCallBegan({
          method: GET_LIST_ITEMS,
          options: {
            listId
          },
          onSuccess: itemsAdded.type
        })
      );
    };
    

    【讨论】:

      猜你喜欢
      • 2018-09-29
      • 2021-01-01
      • 2019-09-22
      • 2017-09-29
      • 2019-03-23
      • 2020-02-22
      • 1970-01-01
      • 2021-12-17
      • 1970-01-01
      相关资源
      最近更新 更多