При использовании избыточности-наблюдаемого из одного действия и одного потока, как я могу распределять различные действия на основе фильтра - PullRequest
0 голосов
/ 30 июня 2018

Обновление

Вот рабочий пример с использованием redux-observable. https://redux -observable-playground-ykzsyp.stackblitz.io Это позволяет добиться того, чего я хочу, используя mergeMap и оператор if / else, но я надеялся использовать Observable.filter, поскольку это выглядит более элегантно.


Оригинальный вопрос

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

const streamEpic = (action$) => action$.pipe(
    ofType('START_PLAYBACK_STREAM'),
    switchMap(playbackStream$),
    filter((playEvent) => playEvent.type === 'LOAD')),
    map((playEvent) => loadActionCreator(playEvent.fileName))
    // how can I filter on other types? 
    // and dispatch other actions?
);

Я видел много примеров rxjs, которые используют один поток и фильтр для отображения различных действий, например:

playbackStream$
    .filter((playEvent) => playEvent.type === 'LOAD'))
    .map((playEvent) => loadActionCreator(playEvent.fileName));

playbackStream$
    .filter((playEvent) => playEvent.type === 'START'))
    .map((playEvent) => startActionCreator());

playbackStream$
    .filter((playEvent) => playEvent.type === 'STOP'))
    .map((playEvent) => stopActionCreator());

Я пытаюсь сделать то же самое в наблюдаемой редуксе, но безуспешно. Если я использую tap, ignoreElements и store.dispatch, я могу заставить работать следующее, но я знаю, что это анти-паттерн.

const streamLoadEvents = (action$, store) => action$.pipe(
    ofType('START_PLAYBACK_STREAM'),
    tap(() => {
        playbackStream$
            .filter((playEvent) => playEvent.type === 'LOAD'))
            .map((playEvent) => store.dispatch(loadActionCreator(playEvent.fileName)));

        playbackStream$
            .filter((playEvent) => playEvent.type === 'START'))
            .map((playEvent) => store.dispatch(startActionCreator()));

        playbackStream$
            .filter((playEvent) => playEvent.type === 'STOP'))
            .map((playEvent) => store.dispatch(stopActionCreator()));
    }),
    ignoreElements()
);

Я знаю, что я мог бы также использовать оператор switch или if / else внутри чего-то вроде map или switchMap, как ответ здесь: Redux-Observable несколько действий в одной эпопее , но я бы хотел чтобы избежать этого, поскольку это довольно не элегантно и не в полной мере использует потоковые операторы. Ответ здесь: https://stackoverflow.com/a/40895613/367766, кажется, приближает меня ...

Какой здесь предложенный подход? Операторы или пример, на который вы можете мне указать? Спасибо!

1 Ответ

0 голосов
/ 13 июля 2018

Вы правы, если / else следует использовать только как последнее средство, и, возможно, есть несколько способов, как вы могли бы подойти к этому, в любом случае, вот мое мнение:

Существует метод слияния , который можно рассматривать как реактивный эквивалент оператора if / else: вы предоставляете ему наблюдаемые, и, если какой-либо из них испускает, результирующая наблюдаемая будет излучать свою собственную стоимость (AC), а также. Когда приходит новое действие, мы проверяем его и выбираем нового создателя действия (AC) соответственно. Давайте посмотрим на некоторый код:

  const filteredAction$ = action$.pipe(
      ofType('START_PLAYBACK_STREAM'),
  );

Ничего интересного здесь, просто отфильтровываем типы действий, которые нам не интересны, а затем

  const operation$ = merge(
      filteredAction$.pipe(
          filter((action) => action.payload.type === 'LOAD'),
          mapTo(loadActionCreator),
      ),
      filteredAction$.pipe(
          filter((action) => action.payload.type === 'START'),
          mapTo(startActionCreator),
      ),
      filteredAction$.pipe(
          filter((action) => action.payload.type === 'STOP'),
          mapTo(stopActionCreator),
      ),
  ).pipe(
      startWith(startActionCreator)
  );

Здесь мы выбираем, какой создатель действия использовать, основываясь на полезной нагрузке действия (обратите внимание, что я следую стандарту flux для действий - данные действия теперь находятся под свойством полезной нагрузки, теперь есть удивительный библиотека , которую вы можете использовать в качестве помощника для этого. Кроме того, вам, вероятно, следует переименовать опору 'type', чтобы избежать путаницы).

mapTo в основном сообщает операции $ stream, что выдавать, вы можете думать об этом выражении как о назначении функции некоторой переменной для последующего использования. Каждый раз, когда вызывается эпопея, мы здесь выберем подходящего создателя действий. startWith здесь на самом деле не нужен (при условии, что первое действие всегда включает тип «START») и предназначен только для семантических целей.

И, наконец, эпос должен вернуть хотя бы одно действие для отправки:

return combineLatest(
      operation$,
      filteredAction$
  )
      .pipe(
          map((arg) => {
            const operation = arg[0];
            const actionObject = arg[1];
            return operation(actionObject);
          })
      )

Последние значения из обоих потоков объединяются вместе, чтобы сформировать последующее действие: операция $ (которая дает нам правильную функцию создателя действия) и FilterAction $ (всегда содержит само действие, нам могут потребоваться некоторые данные из него).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...