Сложные примеры редукционной петли?
Есть ли примеры использования redux-loop
обрабатывать сложные рабочие процессы AJAX? Официальное репо очень минималистично. Из одного примера в дикой природе, который я смог найти ( https://hackernoon.com/why-i-wrote-a-redux-async-outerware-277d450dba74), кажется, что лупа очень похожа к редуксу
Вот несколько примеров сложных рабочих процессов AJAX:
- Ajax Workflow1. Пользователь выбирает два разных фильтра в таблице результатов. Каждый фильтр инициирует запрос ajax, который затем разрешается не по порядку. Таблица результатов должна показывать правильный выбор фильтра. Ошибки не должны обновлять таблицу результатов.
- Ajax Workflow 2
- Пользователь начинает генерацию отчета (это длительный процесс).
- Пользователь переключается на другой отчет. Следует либо отменить, либо проигнорировать ожидающее действие "ждать отчета".
- Более сложный рабочий процесс (на основе старого примера redux-saga)
- Пользователь нажимает кнопку входа в систему, которая запускает ajax-запрос для получения токена авторизации
- Или
- Пользователь сразу нажимает кнопку выхода из системы, которая должна отменить / игнорировать ожидающее действие авторизации
- ИЛИ он должен хранить токен аутентификации при разрешении
- Должен очистить токен авторизации после выхода из системы или в случае ошибки входа
1 ответ
Я дам выстрел на второй рабочий процесс (логин).
Прежде чем идти в код, стоит отметить redux-loop
намного проще и предлагает меньше, чем redux-saga
с точки зрения асинхронного управления потоком. Но в духе Elm
основное внимание уделяется потоку данных - что неудивительно, как правило, достигается типами данных. Поэтому полезно думать с точки зрения статически типизированного языка. В Haskell
или же Elm
Вероятно, полезно смоделировать проблему по типу данных, который сам кодирует конечный автомат:
data LoginStatus data err =
LoggedOut |
, LoggedIn data |
, LoginError err |
, Pending
куда data
а также err
Переменные типа представляют тип данных входа (токены) и ошибки входа. JavaScript, динамически типизируемый, не имеет смысла выражать ту же идею - но есть много динамических приемов, которые можно использовать для имитации теговых типов объединения, таких как LoginStatus
, Без дальнейшего, вот код:
import {match} from "single-key";
export default function reducer(state, action) {
return match(state, {
LoggedOut : () => loggedOutReducer(state, action),
LoggedIn : () => loggedInReducer(state, action),
Pending : () => pendingReducer(state, action),
LoginError : () => loginErrorReducer(state, action)
});
}
Здесь я буду использовать простую и менее известную библиотечную единственную клавишу для достижения базовых типов объединения во время выполнения. "Одноключевой" объект, как следует из названия, - это объект, содержащий только ключ и значение, например { a: 1 }
("а" - это ключ, а 1 - это значение). Мы будем моделировать состояние с помощью одноключевых объектов - разные ключи представляют разные варианты LoginStatus
, Несколько примеров состояний:
{
LoggedOut : true
}
{
LoggedIn : {
token : 1235,
user : { firstName: "John" }
}
}
{
Pending : true
}
После того, как это прояснено, вот субредукторы, используемые в главном редукторе:
// state :: { LoggedIn: {/* some data * } }
function loggedInReducer(state, action) {
if (action.type === LOGOUT) {
return {
LoggedOut : true
};
}
return state;
}
// state :: { Pending : true }
function pendingReducer(state, action) {
if (action.type === LOGIN_SUCCESS) {
return {
LoggedIn : {
token : action.payload.token,
user : action.payload.user
}
};
}
if (action.type === LOGIN_ERROR) {
return {
LoginError : action.payload;
};
}
if (action.type === LOGOUT) {
return {
LoggedOut : true
};
}
return state;
}
// state :: { LoggedOut : true }
function loggedOutReducer(state, action) {
if (action.type === LOGIN) {
return loop({ Pending: true }, Effects.promise(loginRequest));
}
return state;
}
// state :: { LoginError : error }
function loginErrorReducer(state, action) {
if (action.type === LOGIN) {
return loop({ Pending: true }, Effects.promise(loginRequest));
}
return { LoggedOut : true };
}
Это как переходы в конечном автомате, за исключением случаев, когда данные связаны с состоянием. Каждый отдельный редуктор довольно прост и обрабатывает очень мало типов действий. Только два редуктора возвращают эффекты:
return loop({ Pending: true }, Effects.promise(loginRequest));
Это переходит из состояния LoggedOut
/LoginError
в Pending
и укажите некоторые побочные эффекты - которые будут запланированы redux-loop
, Вы даже можете объединить два варианта в один: { LoggedOut : error | null }
, но я чувствую себя отдельно LoginError
состояние выгодно в долгосрочной перспективе.
С некоторым представлением о типах данных эту проблему легче рассуждать, чем кажется на первый взгляд; Вы можете сделать то же самое с редуктором, примерно таким же, и использовать только redux-thunk
,