безопасное использование типа Dispatch с redux-thunk - PullRequest
2 голосов
/ 18 января 2020

Я использую redux-thunk, чтобы использовать asyn c создателей действий. Результат также возвращается соответствующему абоненту.

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 из-за недопустимых типов.

  1. dispatch, возвращаемый useDispatch, не распознается как функция, которая возвращает Promise, и поэтому TypeScript утверждает, что Property 'then' does not exist on type '(dispatch: Dispatch<AnyAction>) => Promise<void>'..
  2. Даже если это будет признано, Обещание должно быть правильно напечатано

Как можно решить эту ситуацию?

Было бы прекрасно для меня создать оболочку вокруг useDispatch или переопределить тип dispatch, но я понятия не имею, как этот тип должен выглядеть в данной конкретной ситуации.

Большое спасибо за любые предложения.

1 Ответ

3 голосов
/ 18 января 2020

useDispatch возвращает тип Dispatch , используемый Redux , поэтому с ним можно отправлять только стандартные действия. Также для отправки групповых действий объявите их тип как ThunkDispatch (из redux-thunk).

ThunkDispatch получает параметры типа для состояния хранилища, дополнительные аргументы thunk и ваш тип действия. Это позволяет отправить 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 также можно сделать вывод из магазина с помощью 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 Пример игровой площадки

...