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

时间:2020-10-21 11:09:55

标签: typescript react-redux react-hooks redux-toolkit

我有一个自定义钩子,该钩子包装了react-redux的{​​{1}}方法,如果被拒绝,则通知我。问题是我无法推断已解析的类型。

useDispatch
// 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的类型有关,但是我无法动弹……

感谢您的帮助

编辑:我已指定操作为异步重击

3 个答案:

答案 0 :(得分:0)

如果您使用的类型为described in the docsuseDispatch类型,我认为您的问题已经解决了:

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中的PayloadForActionTypesExcludingErrorActions类型,但是请记住,此行为在this PR的下一个发行版中会稍有变化将被合并。如果您需要复制该类型,则可能需要在下一个版本中进行调整。

答案 1 :(得分:0)

您的action钩子中的useAction函数是一个函数,其参数与actionCreator相同,并返回一个解析为Promise的{​​{1}}创建的操作的属性。

我不喜欢这里的所有反向推论,也不知道为什么您返回一个1长度元组而不是直接返回函数,但是这种返回类型应该基于当前代码工作。如果从退货中删除payload,则可以删除readonly

as const

Playground Link

我会这样写:

const useAction = <T extends AnyFn>(
    actionCreator: T
): readonly [(...args: Parameters<typeof actionCreator>) => Promise<ReturnType<typeof actionCreator>['payload']>]

Playground Link

答案 2 :(得分:0)

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

感谢@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])
};