使用 redux-toolkit 中的 `unwrapResult` 输入自定义 `useDispatch` 钩子

Typing custom `useDispatch` hook with `unwrapResult` from redux-toolkit

我有一个自定义挂钩,它包装了 react-reduxuseDispatch 方法并在被拒绝时通知我。问题是我无法推断已解析的类型。

// useAction
type AnyFn = (...s: any[]) => any;

export default <T extends AnyFn>(actionCreator: T) => {
  const dispatch = useDispatch<(action: any) => Promise<any>>(); // * Note Promise<any>

  const action = useCallback((...args: Parameters<typeof actionCreator>) => {
    return dispatch(actionCreator(...args))
      .then(result => {
        return unwrapResult(result) // @reduxjs/toolkit
      })
      .catch(err => {
        // the reason this hook is needed
        // notify error ... then
        return Promise.reject(err)
      })
    }, [dispatch])

  return [action] as const;
}
// action
const fetchUsersActionCreator = createAsyncThunk(
  'fetch', (id: number) => Promise.resolve([id]) // return type IUser[]
)

// consumer
const [fetchUsers] = useAction(fetchUsersActionCreator);

useEffect(() => {
  fetchUsers()
    .then((result) => {
      console.log(result.length) // Property 'length' does not exist on type PayloadAction<IUser[]...
    })
}, [fetchUsers])

// But if I unwrap results here the result type is inferred correctly

useEffect(() => {
  fetchUsers()
    .then(unwrapResult)
    .then((result) => {
      console.log(result.length) // OK
    })
}, [fetchUsers])

那么,我如何输入 useAction 钩子,以便从 unwrapResult 的 return 类型推断出已解析的类型?我知道它与 useDispatch 的输入有关,但我不能指手画脚...

感谢任何帮助

编辑:我已经指定该操作是一个异步 thunk

如果您使用的 useDispatch 类型的类型为 described in the docs:

,我认为您的问题会自行解决
import { configureStore } from '@reduxjs/toolkit'
import { useDispatch } from 'react-redux'
import rootReducer from './rootReducer'

const store = configureStore({
  reducer: rootReducer
})

export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>() // Export a hook that can be reused to resolve types

如果这没有帮助,您可以复制并使用来自 the RTK source, but keep in mind that this behaviour will change slightly with the next release when this PRPayloadForActionTypesExcludingErrorActions 类型将被合并。如果您需要复制该类型,您可能需要在下一个版本中对其进行调整。

useAction 钩子中的 action 函数是一个函数,它采用与 actionCreator 和 return 相同的参数,Promise 解析为创建的动作的 payload 属性。

我不喜欢这里所有的向后推理,我不知道你为什么要 return 长度为 1 的元组而不是直接 return 函数,但是此 return 类型应根据您当前的代码工作。如果从 return.

中删除 as const,则可以删除 readonly
const useAction = <T extends AnyFn>(
    actionCreator: T
): readonly [(...args: Parameters<typeof actionCreator>) => Promise<ReturnType<typeof actionCreator>['payload']>]

Playground Link

我会这样写:

const useAction = <Args extends any[], Action extends PayloadAction<any, any>>(
  actionCreator: (...args: Args) => Action
): (...args: Args) => Promise<Action['payload']> => {
  const dispatch = useDispatch<(action: any) => Promise<any>>(); // * Note Promise<any>

  return useCallback(async (...args: Args) => {
    return dispatch(actionCreator(...args))
      .then(result => {
        return unwrapResult(result) // @reduxjs/toolkit
      })
      .catch(err => {
        return Promise.reject(err)
      })
  }, [dispatch])
}

Playground Link

我要回答我自己的问题,它可能对其他试图实现相同功能的人有用。

感谢@phry 和@linda-paiste 的回答,它们为我指明了正确的方向。

import { unwrapResult, AsyncThunk } from '@reduxjs/toolkit';
import { AppDispatch } from '../store'; // type AppDispatch = typeof store.dispatch

// useAction aka useAsyncThunk
export default <Arg, Returned>(
  actionCreator: AsyncThunk<Returned, Arg, {}>
) => {
  const dispatch = useDispatch<AppDispatch>();

  return useCallback((arg: Arg) => {
    return dispatch(actionCreator(arg))
    .then(result => {
      return unwrapResult(result) // @reduxjs/toolkit
    })
    .catch(err => {
      // the reason this hook is needed
      // notify error ... then
      return Promise.reject(err)
    })
  }, [dispatch, actionCreator])
};