Как мне избежать использования отдельных действий _PENDING _FULFILLED и _REJECTED с помощью redux thunk?

Я пишу свои действия и редукторы с помощью преобразователей, которые отправляют действия _PENDING, _FULFILLED и _REJECTED. Однако мне нужно лучшее решение, чтобы избежать шаблонов. Я перехожу на Typescript, который удваивает этот шаблон, требуя интерфейса для каждого действия _PENDING, _FULFILLED и _REJECTED. Это просто выходит из-под контроля. Есть ли способ получить такую ​​же / аналогичную функциональность моего кода, не имея трех типов действий для каждого преобразователя?

localUserReducer.js

const initialState = {
  fetching: false,
  fetched: false,
  user: undefined,
  errors: undefined,
};

export default function (state = initialState, action) {
  switch (action.type) {
    case 'GET_USER_PENDING':
      return {
        ...state,
        fetching: true,
      };
    case 'GET_USER_FULFILLED':
      return {
        ...state,
        fetching: false,
        fetched: true,
        user: action.payload,
      };
    case 'GET_USER_REJECTED':
      return {
        ...state,
        fetching: false,
        errors: action.payload,
      };
    default:
      return state;
  }
}

localUserActions.js

import axios from 'axios';

export const getUser = () => async (dispatch) => {
  dispatch({ type: 'GET_USER_PENDING' });
  try {
    const { data } = await axios.get('/api/auth/local/current');
    dispatch({ type: 'GET_USER_FULFILLED', payload: data });
  } catch (err) {
    dispatch({ type: 'GET_USER_REJECTED', payload: err.response.data });
  }
};

Я могу неправильно понять redux-thunk, поскольку я новичок. Я не понимаю, как я могу отправлять _REJECTED действий, если я использую реализацию Typescript и redux-thunk, описанную здесь: https://redux.js.org/recipes/usage-with-typescript#usage-with-redux-thunk


person devaent    schedule 27.07.2019    source источник


Ответы (1)


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

Я бы рекомендовал передать переходный аспект асинхронных вызовов в данные. Поэтому вместо того, чтобы отмечать свои действия как _PENDING, _FULFILLED и _REJECTED, отметьте свои данные таким образом и выполните одно действие.

localUser.js (новый файл для типа пользователя)

// Use a discriminated union here to keep inapplicable states isolated
type User =
  { status: 'ABSENT' } |
  { status: 'PENDING' } |
  { status: 'FULLFILLED', data: { fullName: string } } |
  { status: 'REJECTED', error: string };

// a couple of constructors for the fullfilled and rejected data
function dataFulFilled(data: { fullName: string }) {
  return ({ status: 'FULLFILLED', data });
}

function dataRejected(error: string) {
  return ({ status: 'REJECTED', error });
}

localUserReducer.js

const initialState: { user: User } = { user: { status: 'ABSENT' } };

export default function (state = initialState, action): { user: User } {
  switch (action.type) {
    case 'USER_CHANGED':
      return {
        ...state,
        user: action.payload
      };
    default:
      return state;
  }
}

localUserActions.js

import axios from 'axios';

export const getUser = () => async (dispatch) => {
  dispatch({ type: 'USER_CHANGED', payload: { status: 'PENDING' } });
  try {
    const { data } = await axios.get('/api/auth/local/current');
    dispatch({ type: 'USER_CHANGED', payload: dataFulFilled(data) });
  } catch (err) {
    dispatch({ type: 'USER_CHANGED', payload: dataRejected(err.response.data) });
  }
};

Это также устранит необходимость в нескольких логических полях (fetching и fetched) и изолирует различные состояния данных от случайного изменения.

Изменения в логике рендеринга будут необходимы, но, скорее всего, это будет улучшение. Вместо комбинаций вложенных операторов if-else с использованием логических значений можно использовать один переключатель для обработки четырех случаев состояния данных.

Затем вы можете вызвать что-то подобное из своей функции рендеринга ...

function userElement(user: User) {
  switch (user.status) {
    case 'ABSENT':
      return <></>;
    case 'PENDING':
      return <div>Fetching user information...Please be patient...</div>;
    case 'FULLFILLED':
      return <div>{user.data.fullName}</div>;
    case 'REJECTED':
      return <h1>The error is: {user.error}</h1>
  }
}

Надеюсь, это поможет. Удачи!

person Tyro Newling    schedule 04.08.2019
comment
Это то, к чему я пришел в конце концов. Спасибо - person devaent; 06.08.2019