使用 redux-toolkit 中的 `unwrapResult` 输入自定义 `useDispatch` 钩子
Typing custom `useDispatch` hook with `unwrapResult` from redux-toolkit
我有一个自定义挂钩,它包装了 react-redux
的 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
的 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 PR 的 PayloadForActionTypesExcludingErrorActions
类型将被合并。如果您需要复制该类型,您可能需要在下一个版本中对其进行调整。
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']>]
我会这样写:
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])
}
我要回答我自己的问题,它可能对其他试图实现相同功能的人有用。
感谢@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])
};
我有一个自定义挂钩,它包装了 react-redux
的 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
的 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 PR 的 PayloadForActionTypesExcludingErrorActions
类型将被合并。如果您需要复制该类型,您可能需要在下一个版本中对其进行调整。
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']>]
我会这样写:
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])
}
我要回答我自己的问题,它可能对其他试图实现相同功能的人有用。
感谢@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])
};