У меня в настоящее время ситуация, при которой мне нужно, чтобы действия Redux выполнялись последовательно. Я взглянул на различные посредники, такие обезьяны-редуксы, которые кажутся прекрасными, если вы знаете, что последующие действия находятся в точке запуска корня (из-за отсутствия лучшего термина).
По существу, я хотел бы поддерживать очередь действий, которые можно добавить в любой момент. Каждый объект имеет экземпляр этой очереди в своем состоянии, и зависимые действия могут быть помещены в очередь, обработаны и удалены соответствующим образом. У меня есть реализация, но при этом я получаю доступ к состоянию в моих создателях действий, которые чувствуют себя как анти-шаблон.
Я попытаюсь дать некоторый контекст в случае использования и реализации.
Использовать регистр
Предположим, вы хотите создать несколько списков и сохранить их на сервере. При создании списка сервер отвечает идентификатором для этого списка, который используется в последующих концах API, относящихся к списку:
http://my.api.com/v1.0/lists/ // POST returns some id
http://my.api.com/v1.0/lists/<id>/items // API end points include id
Представьте, что клиент хочет оптимистично обновлять эти точки API, чтобы усилить UX - никто не любит смотреть на прядильщиков. Поэтому, когда вы создаете список, ваш новый список мгновенно появляется с возможностью добавления элементов:
+-------------+----------+
| List Name | Actions |
+-------------+----------+
| My New List | Add Item |
+-------------+----------+
Предположим, что кто-то пытается добавить элемент до того, как ответ от начального вызова создания вернул его. API-интерфейс элементов зависит от идентификатора, поэтому мы знаем, что мы не можем назвать его до тех пор, пока у нас не будет данных. Тем не менее, мы можем оптимистично показать новый элемент и вызвать вызов API-интерфейсов элементов, чтобы он срабатывал после завершения вызова создания.
Потенциальное решение
Метод, который я использую, чтобы обойти это в настоящее время, - предоставить каждому списку очередь действий, то есть список действий Redux, которые будут запускаться последовательно.
Функциональность редуктора для создания списка может выглядеть примерно так:
case ADD_LIST:
return {
id: undefined, // To be filled on server response
name: action.payload.name,
actionQueue: []
}
Затем, в создателе действия, мы вызываем действие вместо прямого его запуска:
export const createListItem = (name) => {
return (dispatch) => {
dispatch(addList(name)); // Optimistic action
dispatch(enqueueListAction(name, backendCreateListAction(name));
}
}
Для краткости предположим, что функция backendCreateListAction вызывает API-интерфейс fetch, который отправляет сообщения для удаления из списка с успехом/сбой.
Проблема
Что меня беспокоит, так это реализация метода enqueueListAction. Здесь я получаю доступ к состоянию, чтобы управлять продвижением очереди. Это выглядит примерно так (игнорируйте это соответствие по имени - на самом деле это использует clientId, но я стараюсь, чтобы этот пример был прост):
const enqueueListAction = (name, asyncAction) => {
return (dispatch, getState) => {
const state = getState();
dispatch(enqueue(name, asyncAction));{
const thisList = state.lists.find((l) => {
return l.name == name;
});
// If there nothing in the queue then process immediately
if (thisList.actionQueue.length === 0) {
asyncAction(dispatch);
}
}
}
Здесь предположим, что метод enqueue возвращает простое действие, которое вставляет действие async в список actionQueue.
Все это немного похоже на зерно, но я не уверен, есть ли у него другой способ. Кроме того, поскольку мне нужно отправить в asyncActions, мне нужно передать метод отправки до них.
В методе есть аналогичный код для удаления из списка, который запускает следующее действие, если оно существует:
const dequeueListAction = (name) => {
return (dispatch, getState) => {
dispatch(dequeue(name));
const state = getState();
const thisList = state.lists.find((l) => {
return l.name === name;
});
// Process next action if exists.
if (thisList.actionQueue.length > 0) {
thisList.actionQueue[0].asyncAction(dispatch);
}
}
В общем, я могу жить с этим, но я обеспокоен тем, что это анти-шаблон, и в Redux может быть более сжатый, идиоматический способ сделать это.
Любая помощь приветствуется.