Я дам шанс на второй рабочий процесс (логин).
Прежде чем перейти к коду, стоит отметить, что 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)
});
}
Здесь я буду использовать простую и менее известную библиотеку singe-key для достижения самых основных типов времени выполнения профсоюза. Объект «с одним ключом», как его называют, представляет собой объект с одним ключом и значением, например { a: 1 }
(«a» - это ключ, а 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
.
Я ценю ответ, но ... проблема с этим подходом заключается в том, что он смешивает обязанности. Асинхронный рабочий процесс + «нормальное» состояние. Это приводит к сложным редукторам. В реальных проектах это означает, что вы просто упростили создание хрупкого кода. Кроме того, вы не рассмотрели, как на самом деле выполнять описанные выше рабочие процессы. –
Спасибо за отзыв. Это разумная критика. В последнем пункте, я считаю, я в основном рассмотрел, как это будет работать. Хорошо, так как сегодня у меня есть свободное время, просто сделал рабочий пример, используя объяснительную схему редуктора, описанную здесь. У него есть реализация «redux-loop» и «redux-sage» с почти идентичными редукторами: [https://github.com/yiransheng/redux-login-examples](https://github.com/yiransheng/redux -login-examples) –
Что хорошего в вашей идее явного перечисления типов. К сожалению, как говорится в Америке: «Ты избиваешь мертвую лошадь». Кроме того, для смешивания проблем у вас нет специального API для обработки асинхронности. Следовательно, в реальных проектах код становится очень, очень сложным (и повторяющимся). Например, вы не обрабатываете случай отмены. Во-вторых, FRP/sagas выигрывают в тестировании. Вам не нужны издевки. –