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
muslnode 18–226 runs
build_error
glibcnode 18–226 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);
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+.
fixEnsure `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).
fixIf 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.
fixStep 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.
fixEnsure 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. 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.