Отправьте действие в ответ на отмену

Я начал с рецепта отмены из документируемых документов и хочу его немного расширить.

В основном у меня есть сценарий, когда после отмены срабатывает, используя takeUntil Я хочу отправить еще одно действие для очистки и т. Д.

Это то, что я придумал до сих пор: https://jsbin.com/zemenu/195/edit?js,output

Запустите "Получить информацию о пользователе" и нажмите "Отмена". Я хочу, чтобы он выполнял действия в следующем порядке:
- USER/FETCH
- REQUEST/STARTED
- USER/CANCELLED
- REQUEST/CANCELLED

Это работает так, как я настроил прямо сейчас. Но я должен полагаться на прохождение dispatch в requestSequence функция, а затем вызвать его в finally, Есть ли более чистый способ сделать это только с наблюдаемыми операторами? Итак, когда это USER.CANCELLED срабатывает какое-то окончательное действие отображается внутри requestSequence наблюдаемым.

Redux logger включен, поэтому проверьте консоль на наличие всех действий.

1 ответ

Решение

Вместо того, чтобы использовать .takeUntil() звучит так, как будто вы хотите использовать .race(), который довольно удачно назван. Какой бы поток ни исходил первым, побеждает! Другой отписался.

Вам нужно будет немного перестроить некоторые вещи, чтобы использовать их по своему желанию. Вы хотите выделить первое действие, которое вы совершаете немедленно, ваш request.onStart(meta), отдельно от запроса AJAX Observable.fromPromise(apiCall(...args)), Затем вы хотите участвовать в гонке непосредственно между этим ajax и отменой, так что вам нужно будет пройти в action$ ActionsObservable, так как у вас есть все это в помощнике.

https://jsbin.com/suvaka/edit?js,output

function requestSequence(apiCall, args, meta, action$) {
  return Observable.of(request.onStart(meta))
    .concat(
      Observable.fromPromise(apiCall(...args))
        .map((payload) => request.onSuccess(payload, meta))
        .catch((e) => Observable.of(request.onError(e, meta)))
        .race(
          action$.ofType(USER.CANCELLED)
            .map(() => request.onCancel(meta))
        )
    );
}

const fetchUserEpic = (action$, store) =>
  action$.ofType(USER.FETCH)
    .mergeMap(action =>
      requestSequence(
        userRequest, 
        [`/api/users/${action.payload}`],
        { activity: USER.FETCH, path: 'user' },
        action$
      )   
    );

Примечание: будьте осторожны с преждевременными абстракциями, такими как создание таких помощников. Несмотря на то, что вы можете повторять некоторые вещи в некоторых эпосах, я обнаружил, что абстрагирование может усложнить задачу повторения позже, особенно если это кто-то другой, кто не писал код и не был гуру Rx. Только вы можете знать, относится ли этот совет к вам и вашей базе кода, конечно.

Основной сбивающий с толку пункт для меня - все аргументы, которые вы должны передать requestSequence Многим будет трудно понять, когда они впервые столкнутся с этим. Если вы обнаружите, что очень часто ваши эпосы делают одно и то же, и вы хотите использовать их повторно, возможно, абстрагирование всего эпоса будет более ясным, и создайте такие утилиты API, как userRequest ниже, что вы можете проверить самостоятельно.

(непроверенный, в основном псевдокод)

const createApiEpic = options =>
  action$ =>
    action$.ofType(options.on)
      .mergeMap(action =>
        Observable.of(request.onStart(meta))
          .concat(
            options.effect(action.payload)
              .map(payload => request.onSuccess(payload, meta))
              .catch(e => Observable.of(request.onError(e, meta)))
              .race(
                action$.ofType(options.cancel)
                  .map(() => request.onCancel(meta))
              )
          )
      );

const userRequest = id =>
  Observable.ajax.getJSON(`/api/users/${id}`);

const fetchUserEpic = createApiEpic({
  on: USER.FETCH,
  effect: userRequest
  cancel: USER.CANCELLED
});
Другие вопросы по тегам