#typescript #react-redux #react-hooks #redux-toolkit
#typescript #реагировать-redux #реагирующие хуки #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
? Я знаю, что это где-то связано с наборами useDispatch
, но я не могу указать на это…
Любая помощь приветствуется
РЕДАКТИРОВАТЬ: я указал, что действие является асинхронным.
Ответ №1:
Я бы предположил, что ваша проблема уже решилась бы сама собой, если бы вы использовали useDispatch
тип, который вводится, как описано в документах:
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
Если это не поможет, вы можете скопировать и использовать PayloadForActionTypesExcludingErrorActions
тип из источника RTK, но имейте в виду, что это поведение немного изменится со следующим выпуском, когда этот PR будет объединен. Если вам нужно скопировать этот тип, вам, вероятно, потребуется настроить его в следующем выпуске.
Комментарии:
1. Спасибо за ответ, я пробовал это раньше, но
useAppDispatch
даже не похож на обещание, может быть, потому, что у меня расширенный магазин? вывод:const useAppDispatch: () => ThunkDispatch<{ router: ..., user: ... }, null, AnyAction> amp; ThunkDispatch<...> amp; Dispatch<...>
2. Сам по себе useAppDispatch не похож на обещание, но вызов useAppDispatch с помощью thunk в качестве аргумента (и только тогда!) Должен возвращать обещание.
Ответ №2:
action
Функция в вашем useAction
хуке — это функция, которая принимает те же аргументы, actionCreator
что и и возвращает a Promise
, которое преобразуется в payload
свойство созданного действия.
Мне не нравятся все обратные выводы здесь, и я не знаю, почему вы возвращаете кортеж длиной 1 вместо того, чтобы возвращать функцию напрямую, но этот возвращаемый тип должен работать на основе вашего текущего кода. Вы можете удалить readonly
, если вы удалите as const
из своего возврата.
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])
}
Комментарии:
1. Спасибо за ответ, однако разрешенный тип здесь не указан, я создал игровую площадку , и результат по-прежнему любой
2. Да, перенос возвращаемого значения в массив был вызван тем, что раньше у меня было больше элементов, но с тех пор я его удалил.
Ответ №3:
Я собираюсь ответить на свой собственный вопрос, это может быть полезно для других, пытающихся достичь той же функциональности.
Спасибо @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])
};