Использование redux-действий, redux-thunk и redux-обещания-промежуточного программного обеспечения в машинописи

Новое в машинописной + редуксной экосистеме здесь.

Как правильно инкапсулировать информацию о типе в асинхронные действия, когда в TypeScript используются избыточные-действия, избыточное-промежуточное и избыточное-обещание-промежуточное программное обеспечение?

Пример аутентификации:

/* actions */
const login = createAction(LOGIN, authService.login);


/* authService */
async function login(payload: LoginPayload): Promise<LoginResponse> {
 // ... authenticate here.
}

Поскольку я использую промежуточное программное обеспечение redux-обещание, действия LOGIN_PENDING, LOGIN_FULFILLED/LOGIN_REJECTED отправляются автоматически. Как мне создать типы для них, чтобы редуктор мог выяснить, с каким объектом действия он имеет дело?

Поскольку редукс-действия следует за FSA, _FULFILLED должен иметь action.payload, _REJECTED должен иметь action.error

/* reducer */
function loginReducer(state: AppState, action: AuthAction) {
  switch (action.type) {
    case LOGIN_FULFILLED:
      // action.payload should be defined as LoginResponse object here.
      // action.error shouldnt be present.
    case LOGIN_REJECTED:
      // action.error should be defined
  }
}

Как бы я пошел о создании AuthAction тип? Я предполагаю, что это должен быть тип объединения каждого из отдельных типов действий (которые могут быть типами объединения самостоятельно). redux-actions также обеспечивает Action а также BaseAction типы для этого.

1 ответ

"Нормальный" способ сделать это - указать все "интерфейсы действий" и тип объединения вплоть до редуктора. Затем включите тип. Но из примера кода не ясно, является ли тип AuthAction типом объединения...

например

type T = Object; //Your resolve data type
interface ILoginAction {type: "LOGIN", payload: {promise: Promise<T> }}
interface ILoginRejectedAction {type: "LOGIN_REJECTED", error: YourErrorType }
interface ILoginFulfilledAction {type: "LOGIN_FULFILLED", payload: {data: T }}

export type LoginActions = ILoginAction | ILoginRejectedAction | ILoginFulfilledAction

Разбавление:

import { LoginActions } from "./actions"; //Or where your actions are
function loginReducer(state: AppState, action: LoginActions) {
  switch (action.type) {
    case "LOGIN":
      // action.payload should be defined as LoginResponse object here.
      // action.error shouldnt be present.
    case "LOGIN_REJECTED":
      // action.error should be defined
  }
}

Вероятно, вы можете создать этот союз более умным способом, используя некоторые обобщения, но это ручной подход.

Я бы рекомендовал использовать createAsyncAction вместо того createAction. Таким образом, вы можете определить типизацию следующим образом:

/* actions */
const login = createAsyncAction<
  typeof LOGIN,
  LoginResponse, // no need to wrap in Promise<>
  null, // this is optional Meta,
  [LoginPayload] // action creator arguments wrapped in array
>(LOGIN, authService.login);

Но боюсь, у меня нет ответа, когда login это преобразователь:

/* actions */
const loginAction = createAsyncAction(LOGIN, authService.login)
const loginActionCreator = (data: LoginPayload) => async(dispatch: Dispatch) => {
  await dispatch(loginAction(data));
}
// ERROR: () => void is not assignable to () => (dispatch) => void (pseudo code)
Другие вопросы по тегам