Registry / web-framework / redux-observable

redux-observable

JSON →
library3.0.0-rc.3jsnpmunverified

redux-observable is an RxJS-based middleware for Redux, designed to manage complex asynchronous side effects and compose/cancel async actions using "Epics." Epics are functions that take a stream of actions (action$) and the current state as an observable (state$), returning a stream of actions, enabling powerful reactive programming patterns within a Redux application. It offers a declarative alternative to redux-thunk or redux-saga by leveraging RxJS operators for filtering, transforming, and orchestrating action streams. The current latest version is 3.0.0-rc.3, actively in development, which maintains compatibility with RxJS v7. Previous stable versions like 2.x.x also supported RxJS v7. The project generally has an as-needed release cadence, focusing on critical fixes and peer dependency compatibility. Key differentiators include its tight integration with the RxJS ecosystem, providing robust tools for cancellation, debouncing, and complex observable-based logic that might be more verbose or imperative with other middleware solutions.

npm install redux-observable
INSTALL
IMPORT
SIG · REDUX-OBSERVABLE
R
redux-observable
web-frameworkjavascriptv3.0.0-rc.3
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

createEpicMiddleware
import { createEpicMiddleware } from 'redux-observable'
const { createEpicMiddleware } = require('redux-observable')
ESM import is preferred. CommonJS require() pattern is for older Node.js environments or build setups that transpile ESM to CJS. Since v1, createEpicMiddleware no longer takes the root epic directly; you must call .run(rootEpic) on the middleware instance after store creation.
combineEpics
import { combineEpics } from 'redux-observable'
const { combineEpics } = require('redux-observable')
Used to compose multiple individual epics into a single root epic. ESM import is standard.
ofType
import { ofType } from 'redux-observable/operators'
import { ofType } from 'redux-observable'
Since v2, ofType is a pipeable operator and must be imported from 'redux-observable/operators', not directly from 'redux-observable'. In v1.x, it was available directly on the action$ observable via ActionsObservable.
Epic
import { type Epic } from 'redux-observable'
import { Epic } from 'redux-observable'
This is a TypeScript type for defining an Epic. Use 'type' import for clarity and to ensure it's removed at compile-time if your TypeScript version and configuration supports it.

Demonstrates setting up a Redux store with redux-observable middleware, defining an action, a reducer, and an 'epic' to handle an asynchronous user fetch with error handling, logging state changes over time.

import { createStore, applyMiddleware, combineReducers } from 'redux'; import { createEpicMiddleware, combineEpics, Epic } from 'redux-observable'; import { of, from } from 'rxjs'; import { catchError, mergeMap, ofType, tap, map } from 'rxjs/operators'; // --- 1. Define Actions --- interface FetchUserRequestAction { type: 'FETCH_USER_REQUEST'; payload: string; } interface FetchUserSuccessAction { type: 'FETCH_USER_SUCCESS'; payload: { id: string; name: string; }; } interface FetchUserFailureAction { type: 'FETCH_USER_FAILURE'; payload: string; } type UserAction = FetchUserRequestAction | FetchUserSuccessAction | FetchUserFailureAction; const fetchUserRequest = (userId: string): FetchUserRequestAction => ({ type: 'FETCH_USER_REQUEST', payload: userId, }); const fetchUserSuccess = (user: { id: string; name: string; }): FetchUserSuccessAction => ({ type: 'FETCH_USER_SUCCESS', payload: user, }); const fetchUserFailure = (error: string): FetchUserFailureAction => ({ type: 'FETCH_USER_FAILURE', payload: error, }); // --- 2. Define Reducer --- interface UserState { user: { id: string; name: string; } | null; loading: boolean; error: string | null; } const initialState: UserState = { user: null, loading: false, error: null, }; const userReducer = (state: UserState = initialState, action: UserAction): UserState => { switch (action.type) { case 'FETCH_USER_REQUEST': return { ...state, loading: true, error: null }; case 'FETCH_USER_SUCCESS': return { ...state, loading: false, user: action.payload }; case 'FETCH_USER_FAILURE': return { ...state, loading: false, error: action.payload }; default: return state; } }; const rootReducer = combineReducers({ user: userReducer, }); export type RootState = ReturnType<typeof rootReducer>; // --- 3. Define Epic --- // A mock API call const fetchUserApi = (userId: string): Promise<{ id: string; name: string; }> => { console.log(`[Epic] Simulating API call for user: ${userId}`); return new Promise((resolve, reject) => { setTimeout(() => { if (userId === '123') { resolve({ id: '123', name: 'John Doe' }); } else if (userId === 'error') { reject('User not found or network error'); } else { resolve({ id: userId, name: `User ${userId}` }); } }, 1000); }); }; const fetchUserEpic: Epic<UserAction, UserAction, RootState> = (action$, state$) => action$.pipe( ofType<UserAction, 'FETCH_USER_REQUEST'>('FETCH_USER_REQUEST'), tap(action => console.log(`[Epic] Caught FETCH_USER_REQUEST for ID: ${action.payload}`)), mergeMap(action => from(fetchUserApi(action.payload)).pipe( // Convert Promise to Observable map(user => fetchUserSuccess(user)), catchError(error => of(fetchUserFailure(error.toString()))) ) ) ); // --- Combine all epics --- const rootEpic = combineEpics( fetchUserEpic ); // --- 4. Create Store and run Epic Middleware --- const epicMiddleware = createEpicMiddleware<UserAction, UserAction, RootState>(); const store = createStore( rootReducer, applyMiddleware(epicMiddleware) ); epicMiddleware.run(rootEpic); // --- 5. Dispatch Actions --- console.log('Initial state:', store.getState()); store.dispatch(fetchUserRequest('123')); setTimeout(() => { console.log('State after first request:', store.getState()); store.dispatch(fetchUserRequest('456')); }, 1500); setTimeout(() => { console.log('State after second request:', store.getState()); store.dispatch(fetchUserRequest('error')); }, 3000); setTimeout(() => { console.log('Final state:', store.getState()); }, 4500);
Debug
Known issues
breakingThe `ofType()` operator, introduced in v1, was a method on `action$` (`ActionsObservable`). In v2 and later, `ofType` became a pipeable RxJS operator and must be explicitly imported from `redux-observable/operators`.
fix
Change `action$.ofType(...)` to `action$.pipe(ofType(...))` and `import { ofType } from 'redux-observable/operators';`
affects: >=2.0.0
breakingThe `createEpicMiddleware` API changed in v1. You no longer pass your `rootEpic` directly to `createEpicMiddleware()`. Instead, you must call `epicMiddleware.run(rootEpic)` after the Redux store has been created and middleware applied.
fix
Update your middleware setup from `createEpicMiddleware(rootEpic)` to `const epicMiddleware = createEpicMiddleware(); const store = createStore(reducer, applyMiddleware(epicMiddleware)); epicMiddleware.run(rootEpic);`
affects: >=1.0.0
breakingredux-observable v1 requires RxJS v6.x, and v2/v3 require RxJS v7.x. Incompatible RxJS versions will lead to runtime errors or missing operators.
fix
Ensure your RxJS version matches the peer dependency of your installed `redux-observable` version. For v2+, install `rxjs@^7.0.0`.
affects: >=1.0.0
gotchaErrors thrown or unhandled within an Epic's observable stream can terminate the entire stream, preventing the epic from reacting to future actions. Proper error handling (e.g., using `catchError` within inner streams) is crucial.
fix
Always place `catchError()` within `mergeMap` or `switchMap` operators, *after* the asynchronous operation, to prevent the main `action$` stream from terminating. Emit an error action or handle the error gracefully.
affects: >=0.14.0
gotchaEpics receive actions *after* they have passed through reducers. An Epic that simply returns its input `action$` stream (e.g., `action$ => action$`) will create an infinite loop of dispatches.
fix
Epics must transform or filter actions. If an epic doesn't need to produce an output action, use `ignoreElements()` or ensure it completes/filters appropriately to prevent feedback loops.
affects: >=0.1.0
breakingDirect access to `store.dispatch()` and `store.getState()` as epic arguments was removed in v1. Instead, epics receive a `StateObservable` (aliased as `state$`) which provides the current state via `state$.value` and can be composed as an Observable.
fix
Use `state$.value` for imperative state access or compose `state$` with other observables for reactive state changes. Emit new actions via the epic's return stream instead of `dispatch()`.
affects: >=1.0.0
Errors
Common errors & fixes
TypeError: action$.ofType(...).switchMap is not a function
Using RxJS v5/6 prototype operators with `redux-observable` v1+ that expects pipeable operators, or using old `ofType` syntax with v2+.
fix
Ensure `redux-observable` version matches your RxJS version (v1 for RxJS 6, v2+ for RxJS 7). Use pipeable operators like `action$.pipe(ofType(...), switchMap(...))` and import `ofType` from `redux-observable/operators`.
Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported
Attempting to `require()` an ESM-only module or a package configured for ESM in a CommonJS context (e.g., Node.js with default module resolution).
fix
If your project is ESM, use `import` statements. If CJS, check if `redux-observable` or its dependencies offer a CJS build or configure your build system (e.g., Webpack, Rollup) to handle module resolution correctly. Ensure `package.json` `type: 'module'` is set if using ESM in Node.
TypeError: Cannot read property 'subscribe' of undefined
An RxJS operator or function that expects an Observable was provided `undefined`, often due to an API call returning nothing or a stream terminating unexpectedly.
fix
Step through the epic logic with a debugger to identify which observable source or operator is producing `undefined` instead of an Observable. Ensure all branches of your epic return a valid Observable.
Type 'Observable<any>' is not assignable to type 'Epic<Action, Action, State>'
Incorrect TypeScript typing for an Epic, or an Epic function returning a type that doesn't match the `Epic` generic signature.
fix
Ensure your Epic function explicitly returns `Observable<Action>` and adheres to the `Epic<InputActions, OutputActions, State>` generic signature. For example, use `ofType<ActionType, 'SOME_ACTION'>('SOME_ACTION')` for better type narrowing.
Upgrade
Version history
3.0.0-rc.3latest on npm
Audit
Dependencies
reduxrequiredCore state management library that redux-observable integrates with. Version 5.x is a peer dependency for v3.
rxjsrequiredCore reactive programming library providing Observables and operators. Version 7.x is a peer dependency for v3.
Agent activity
7 hits · last 30 days
node
6
Amazon
1
Resources
redux-observable — npm install redux-observable · libregistry