наблюдаемый при редукции

Созданное Джеем Фелпсом и мной для использования в наших проектах в Netflix, redux-observable - это промежуточное ПО для redux, вдохновленное redux-thunk.

Redux-observable позволяет разработчикам отправлять функцию, которая возвращает наблюдаемое, обещание или итеративное действие (я). Когда наблюдаемый объект испускает действие, или обещание разрешает действие, или итерируемый объект выдает действие, это действие затем отправляется как обычно.

Это обеспечивает:

  • Средство использования реактивного программирования и композиции для создания асинхронных эффектов, которые отправляют действия вашему редуктору (-ам).
  • Простой способ отменить эти асинхронные действия с помощью реактивной композиции.
  • Более явный путь к отмене путем вызова `unsubscribe` для объекта подписки, возвращенного из отправки.
  • Средство объединения любых действий в отдельные потоки со сложными операциями соединения, такими как `zip` и` combWith`.

redux-observable написан с использованием RxJS 5 под капотом и, как таковой, работает с любым наблюдаемым типом, который реализует `Symbol.observable`, таким как RxJS , Kefir и (скоро) Most . Однако RxJS 5 является зависимостью от redux-observable.

ПРИМЕЧАНИЕ. Часть того, что ниже, устарела, обратитесь к документации на Github.

Технический обзор

Чтобы получить полный пример, я рекомендую проверить пример, представленный в репозитории, но для базового использования это общая идея:

Вы можете отправить функцию, которая возвращает любое наблюдаемое:

const test = () => 
  Observable.of({ type: ‘TEST’, data: ‘hello world’ });
dispatch(test);

или любое обещание:

const test = () => 
  Promise.resolve({ type: ‘TEST’, data: ‘hello world’ });
dispatch(test);

или любой итерируемый (например, массив или генератор):

const manyActions = () => [{ type: 'TEST1' }, { type: 'TEST2' }];
dispatch(manyActions);

Когда вы отправляете функцию, она возвращает объект подписки, который можно использовать для отмены асинхронного действия. Это позволяет вам явным образом отказаться от подписки на асинхронное действие позже и отменить его.

const startTicking = () => 
  Observable.interval(1000)
    .map((i) => ({ type: 'TICK', i });
const sub = dispatch(startTicking);

// later you can cancel the dispatched async action
sub.unsubscribe();

Однако это может быть не лучший подход. redux-observable также предоставляет два аргумента функции, которую вы передаете в `dispatch`. Первый аргумент - это наблюдаемый поток всех отправленных будущих действий. Это позволяет разработчику составлять вместе множество сложных асинхронных поведений, включая отмену, которая может быть выполнена с помощью оператора takeUntil в RxJS. Для эргономики наблюдаемые действия имеют оператор ofType, позволяющий более четко фильтровать определенные типы отправляемых действий.

const startTicking = (actions, store) => 
  Observable.interval(1000)
    .map((i) => ({ type: 'TICK', i }))
    .takeUntil(actions.ofType('STOP_TICK'));
dispatch(startTicking);

// to stop the ticking actions at a later point
dispatch({ type: 'STOP_TICK' });

Чтобы быть немного более целостным, вот пример создания запроса ajax, который можно прервать, отправив действие прерывания:

// Just like normal redux, we're now using an action factory
// so that we can create a one-off observable that relies on userId
const fetchUserById = (userId) => (
  (actions) => (
    Observable.ajax(`/api/users/${userId}`)
      .map(
        (payload) => ({ type: 'FETCH_USER_FULFILLED', payload })
      )
      .takeUntil(actions.ofType('FETCH_USER_ABORT'))
      .startWith({ type: 'FETCH_USER_PENDING' })
  )
);

const subscription = dispatch(fetchUserById(1));

// To cancel the AJAX request you can dispatch an abort action
// from anywhere in your app
dispatch({ type: 'FETCH_USER_ABORT' });

// or if it happens to be more ergonomic, just unsubscribe
// directly. Sometimes you want to abort all of these,
// sometimes just this single one.
subscription.unsubscribe();

Происхождение

Первоначально основанные на реакции приложения dataviz, над которыми мы с Джеем работали здесь, в Netflix, использовали простые идиомы React, такие как передача props и `setState` для управления состоянием в наших представлениях. Хотя это работало и было достаточно эргономичным, мы быстро оказались в траве, поскольку некоторые из наших более сложных представлений и визуализаций росли.

Мы изучили другие решения, такие как redux-thunk и redux-saga, но, учитывая, что нам действительно нравится Rx и нравится использовать реактивное программирование для решения сложных асинхронных проблем, представленных приложениями dataviz в реальном времени, мы решили попробовать создать новое промежуточное программное обеспечение. который чисто решил нашу проблему с использованием RxJS 5.

Почему Redux? Почему не RxJS 5?

Учитывая, что «Redux может быть записан в несколько строк Rx с использованием оператора сканирования», зачем вообще использовать Redux?

Ответ на этот вопрос довольно прост: Redux имеет четко определенный существующий шаблон и руководство по использованию в React. Что еще более важно, у Redux есть много хороших инструментов, управляемых сообществом, которыми мы хотели воспользоваться. В конце концов, не имеет значения, запускаются ли редукторы через Rx `scan` или Redux. Важны продуктивность и производительность, а инструменты Redux и Redux хорошо обеспечивают эти две вещи.