Redux Observable Middleware

raw JSON →
0.2.0 verified Thu Apr 23 auth: no javascript abandoned

Redux Observable Middleware is a lightweight Redux middleware designed for integrating RxJS-like observables directly into Redux action creators. It is currently in an early stage of development, with the latest stable version being `0.2.0`, published 10 years ago. While there isn't a defined release cadence, its purpose is to simplify handling asynchronous data streams by automatically subscribing to any object with a `subscribe` method found within an action's `observable` property. This package dispatches specific lifecycle actions (`_ON_NEXT`, `_ON_ERROR`, `_ON_COMPLETED` or custom types) as the observable emits values, completes, or errors. This approach differs from more comprehensive solutions like `redux-observable`, which utilizes "epics" for side effects, offering a simpler, direct method for incorporating observables into the Redux data flow.

error TypeError: observable.subscribe is not a function
cause The `observable` property in your dispatched action is not a valid Observable-like object (i.e., it does not have a `subscribe` method). This could be due to forgetting to include the `observable` property, passing `undefined`, or passing a non-observable object.
fix
Ensure that the observable property in your Redux action is an instance of an RxJS Observable or any object that implements a subscribe method. Double-check that rxjs is correctly imported and Observable instances are created correctly.
error Actions with type `MY_ACTION_TYPE` are not handled, but `MY_ACTION_TYPE_ON_NEXT` is.
cause When using a string for the `type` property (e.g., `'MY_ACTION_TYPE'`), the middleware dispatches the original action type immediately (`ACTION_A`), then suffixes for `ON_NEXT`, `ON_ERROR`, `ON_COMPLETED`. If you are only listening for `ON_NEXT` etc., you might miss the initial action. If `type` is an object, only specified keys like `onNext` will dispatch.
fix
If using a string type, ensure your reducer handles both the original ACTION_TYPE and its suffixed variants (e.g., ACTION_TYPE_ON_NEXT). If using an object type, be explicit about which lifecycle actions you want to dispatch by defining onSubscribe, onNext, onError, onCompleted properties in the type object of your action.
gotcha The package `redux-observable-middleware` has not been updated in a very long time (last publish 10 years ago for v0.2.0) and uses an older style of RxJS (implied RxJS 4/5 by the `Rx.Observable.interval` syntax). Modern RxJS (v6+) uses pipeable operators (`.pipe(operator())`) and has different import paths and usage patterns. Compatibility with newer RxJS versions is not guaranteed and requires careful adaptation.
fix Consider migrating to `redux-observable` (the actively maintained alternative) or extensively testing compatibility with a modern RxJS setup, adapting observable creation and operator chaining.
breaking The behavior of `onSubscribe` actions was refined in v0.2.0 to ensure they are always dispatched first, regardless of the observable type. While intended as a fix, this is a behavioral change that might affect logic relying on the previous, less consistent dispatch order for `onSubscribe` actions.
fix Review any existing logic that specifically depends on the timing or presence of the `onSubscribe` action relative to other lifecycle actions for an observable. Ensure your state updates or side effects correctly account for the `onSubscribe` action always being the first dispatched.
gotcha Error handling within the observable is crucial. If an observable errors out and is not caught, it can lead to uncaught exceptions or the observable stream terminating prematurely, preventing further actions from being processed for that specific observable. The middleware itself provides an `_ON_ERROR` action, but the observable stream itself needs internal `catchError` operators for resilience.
fix Always include `catchError` operators within your RxJS observable chains to handle errors gracefully before they propagate out of the observable. Dispatch an appropriate action within `catchError` to inform the Redux store of the error, as shown in `redux-observable` patterns. For example: `observable.pipe(map(data => ({ type: 'ON_NEXT', data })), catchError(err => of({ type: 'ON_ERROR', err })))`.
npm install redux-observable-middleware
yarn add redux-observable-middleware
pnpm add redux-observable-middleware

This quickstart demonstrates how to integrate an RxJS observable into a Redux action using `redux-observable-middleware`. It sets up a basic Redux store with the middleware, dispatches an action containing an observable that emits values every second, and logs the state changes as the observable progresses through its `ON_NEXT`, `ON_ERROR`, and `ON_COMPLETED` lifecycle actions.

import * as Redux from 'redux';
import * as Rx from 'rxjs';
import observableMiddleware from 'redux-observable-middleware';

const ACTION_TYPE = 'INTERVAL';

function reducer(state = null, action) {
  console.log('Action received:', action.type, action.data || action.err);
  switch (action.type) {
    case `${ACTION_TYPE}_ON_NEXT`:
      return action.data;
    case `${ACTION_TYPE}_ON_ERROR`:
      console.error('Observable error:', action.err);
      return state; // Or handle error appropriately
    case `${ACTION_TYPE}_ON_COMPLETED`:
      console.log('Observable completed.');
      return state;
    default:
      return state;
  }
}

const store = Redux.createStore(reducer, Redux.applyMiddleware(observableMiddleware));

store.subscribe(() => {
  console.log('Current state:', store.getState());
});

console.log('Dispatching observable action...');
store.dispatch({
  type: ACTION_TYPE,
  observable: Rx.interval(1000).pipe(Rx.take(5))
});