使用redux-thunk进行类型安全的useDispatch

人气:858 发布:2022-10-16 标签: typescript redux redux-thunk

问题描述

我正在使用redux-thunk来使用异步操作创建器。结果也会返回给各自的调用方。

function fetchUserName(userId: number): Promise<string> {
  return Promise.resolve(`User ${userId}`)
}

function requestUserName(userId: number) {
  return (dispatch: Dispatch) => {
    return fetchUserName(userId).then(name => {
      dispatch({
        type: 'SET_USERNAME',
        payload: name,
      })
    })
  }
}

这样可以更新存储区,同时允许组件直接处理响应。

function User() {
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(requestUserName(1))
      .then(name => {
        console.log(`user name is ${name}`)
      })
      .catch(reason => {
        alert('failed fetching user name')
      })
  }, [])
}

这按预期工作,但由于类型无效,它将不会由TypeScript编译。

useDispatch返回的dispatch未被识别为返回承诺的函数,因此TypeScript认为Property 'then' does not exist on type '(dispatch: Dispatch<AnyAction>) => Promise<void>'.。 即使它会被识别,也应该正确键入承诺

如何解决此情况?

我可以围绕useDispatch创建包装器,或者重新定义dispatch的类型,但我不知道该类型在这种特定情况下应该是什么样子。

非常感谢您的建议。

推荐答案

useDispatch返回Dispatch类型used by Redux,因此只能随其一起调度标准动作。若要也分派thunk操作,请将其类型声明为ThunkDispatch(来自redux-thunk)。

ThunkDispatch接收存储状态的类型参数extra thunk args和您的操作类型。可以派发一个ThunkAction,这基本上是requestUserName的内部函数。

例如,您可以这样键入:

import { ThunkDispatch } from "redux-thunk";
import { AnyAction } from "redux";

type State = { a: string }; // your state type
type AppDispatch = ThunkDispatch<State, any, AnyAction>; 
// or restrict to specific actions instead of AnyAction

function User() {
  const dispatch: AppDispatch = useDispatch();
  useEffect(() => {
    dispatch(requestUserName(1))
      .then(...)  // works now
  }, []);
  ...
}

AppDispatch也可以inferred从商店购买typeof store.dispatch

import thunk, { ThunkDispatch, ThunkMiddleware } from "redux-thunk";

const mw: ThunkMiddleware<State, AnyAction> = thunk;
const dummyReducer = (s: State | undefined, a: AnyAction) => ({} as State);
const store = createStore(dummyReducer, applyMiddleware(mw));

type AppDispatch = typeof store.dispatch // <-- get the type from store

TS Playground sample

另请参阅Redux有关将TypeScript与挂钩一起使用的文档:https://redux.js.org/usage/usage-with-typescript#define-typed-hooks

519