Вызов эпиков из других эпиков

Во-первых, много реквизита для этой замечательной библиотеки! Я все еще борюсь с вариантом использования. Я хотел бы сначала вызвать другой эпик и дождаться его завершения, прежде чем продолжить текущий эпик. Допустим, у меня есть форма, в которой пользователь может что-то изменить. Перед загрузкой других данных в форму я хочу сначала сохранить текущие изменения. Есть мысли по этому поводу?


person robinvdvleuten    schedule 16.02.2017    source источник


Ответы (2)


Звучит так, как будто вы хотите: если один эпик хочет запустить работу другого эпика и дождаться его завершения, прежде чем продолжить.

Один из подходов (с использованием псевдоимен) заключается в том, что при получении начального действия LOAD_OTHER_DATA вы сразу же начинаете прослушивать одно SAVE_FORM_FULFILLED, которое сигнализирует о том, что форма была сохранена (мы начнем чуть позже). При получении мы mergeMap (или switchMap, в данном случае не имеет значения), что в наш вызов загружаем остальные данные loadOtherDataSomehow() и занимаемся обычным делом. Наконец, хитрость, позволяющая начать фактическое сохранение формы, которую мы ожидаем, заключается в добавлении startWith() в конце всей этой цепочки, которая будет генерировать и отправлять действие для фактического сохранения формы.

const saveFormEpic = (action$, store) =>
  action$
    .ofType('SAVE_FORM')
    .switchMap(() =>
      saveFormSomeHow(store.getState().formDataSomewhere)
        .map(details => ({
          type: 'SAVE_FORM_FULFILLED'
        }))
    );

const loadOtherDataEpic = action$ =>
  action$
    .ofType('LOAD_OTHER_DATA')
    .switchMap(() =>
      action$.ofType('SAVE_FORM_FULFILLED')
        .take(1) // don't listen forever! IMPORTANT!
        .mergeMap(() =>
          loadOtherDataSomeHow()
            .map(otherData => ({
              type: 'LOAD_OTHER_DATA_FULFILLED',
              payload: otherData
            }))
        )
        .startWith({
          type: 'SAVE_FORM'
        })
    );

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

person jayphelps    schedule 16.02.2017
comment
вау спасибо за обстоятельный ответ! Можно вдобавок к этому еще один небольшой вопрос? Сохранение не является обязательным, так как если пользователь не сохранил его сам, я бы хотел, чтобы эпик сделал это за него. Итак, я посмотрел на метод Observer.if() и похоже, что он подходит, но я не могу понять, как сделать это опционально в loadOtherDataEpc. - person robinvdvleuten; 17.02.2017
comment
Просто используйте простой оператор if/else - person jayphelps; 18.02.2017
comment
Именно то, что я искал! Еще раз спасибо за обстоятельный ответ! - person robinvdvleuten; 19.02.2017
comment
@robinvdvleuten всегда пожалуйста! пожалуйста, примите этот ответ, когда у вас будет шанс :) - person jayphelps; 20.02.2017

@jayphelps, я просто хотел сказать спасибо за все вопросы, на которые вы ответили, они действительно помогли мне решить многие мои проблемы.

Я пытался сформулировать свой вопрос и обнаружил, что вы уже ответили на него здесь и сумели использовать свой образец для решения моей проблемы.

В моем примере есть/было 2 expics, которые я хочу/редактор вызывать друг после друга:

const requestNameEpic = action$ =>
  action$.ofType("REQUEST_NAME")
    .switchMap(({id}) =>
      Observable.of(`name returned by 'ajax' call, using id: ${id}`) // Ajax simulation
        .delay(1000)
        .map(name => ({type: "LOAD_NAME", name}))
    );

const requestAgeEpic = action$ =>
  action$.ofType("REQUEST_AGE")
    .switchMap(({name}) =>
      Observable.of(`age returned by 'ajax' call, using name: [${name}]`)
        .delay(1000)
        .map(age => ({type: "LOAD_AGE", age}))
    );

Как мне удалось решить это, используя:

const requestDetailsEpic = action$ =>
  action$.ofType("LOAD_NAME")
    .map(({name}) => ({type: "REQUEST_AGE", name}));

Таким образом, отправка REQUEST_NAME также отправит REQUEST_AGE, но тогда я никогда не смогу отправить только LOAD_NAME.

Я реорганизовал ваш образец, чтобы получить:

const requestDetailsEpic = action$ =>
  action$.ofType("REQUEST_DETAILS")
    .switchMap(({id}) =>
      action$.ofType("LOAD_NAME")
        .take(1) // don't listen forever! IMPORTANT!
        .map(({name}) => ({type: "REQUEST_AGE", name}))
        .startWith({type: "REQUEST_NAME", id})
  );

Еще раз спасибо за всю большую работу, которую вы проделали над redux-observable, и за всю поддержку сообщества.

person Eli    schedule 30.03.2018