{"library":"redux-observable","title":"Redux-Observable","description":"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.","language":"javascript","status":"active","last_verified":"Wed Apr 22","install":{"commands":["npm install redux-observable"],"cli":null},"imports":["import { createEpicMiddleware } from 'redux-observable'","import { combineEpics } from 'redux-observable'","import { ofType } from 'redux-observable/operators'","import { type Epic } from 'redux-observable'"],"auth":{"required":false,"env_vars":[]},"quickstart":{"code":"import { createStore, applyMiddleware, combineReducers } from 'redux';\nimport { createEpicMiddleware, combineEpics, Epic } from 'redux-observable';\nimport { of, from } from 'rxjs';\nimport { catchError, mergeMap, ofType, tap, map } from 'rxjs/operators';\n\n// --- 1. Define Actions ---\ninterface FetchUserRequestAction { type: 'FETCH_USER_REQUEST'; payload: string; }\ninterface FetchUserSuccessAction { type: 'FETCH_USER_SUCCESS'; payload: { id: string; name: string; }; }\ninterface FetchUserFailureAction { type: 'FETCH_USER_FAILURE'; payload: string; }\n\ntype UserAction = FetchUserRequestAction | FetchUserSuccessAction | FetchUserFailureAction;\n\nconst fetchUserRequest = (userId: string): FetchUserRequestAction => ({\n  type: 'FETCH_USER_REQUEST',\n  payload: userId,\n});\n\nconst fetchUserSuccess = (user: { id: string; name: string; }): FetchUserSuccessAction => ({\n  type: 'FETCH_USER_SUCCESS',\n  payload: user,\n});\n\nconst fetchUserFailure = (error: string): FetchUserFailureAction => ({\n  type: 'FETCH_USER_FAILURE',\n  payload: error,\n});\n\n// --- 2. Define Reducer ---\ninterface UserState {\n  user: { id: string; name: string; } | null;\n  loading: boolean;\n  error: string | null;\n}\n\nconst initialState: UserState = {\n  user: null,\n  loading: false,\n  error: null,\n};\n\nconst userReducer = (state: UserState = initialState, action: UserAction): UserState => {\n  switch (action.type) {\n    case 'FETCH_USER_REQUEST':\n      return { ...state, loading: true, error: null };\n    case 'FETCH_USER_SUCCESS':\n      return { ...state, loading: false, user: action.payload };\n    case 'FETCH_USER_FAILURE':\n      return { ...state, loading: false, error: action.payload };\n    default:\n      return state;\n  }\n};\n\nconst rootReducer = combineReducers({\n  user: userReducer,\n});\n\nexport type RootState = ReturnType<typeof rootReducer>;\n\n// --- 3. Define Epic ---\n// A mock API call\nconst fetchUserApi = (userId: string): Promise<{ id: string; name: string; }> => {\n  console.log(`[Epic] Simulating API call for user: ${userId}`);\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      if (userId === '123') {\n        resolve({ id: '123', name: 'John Doe' });\n      } else if (userId === 'error') {\n        reject('User not found or network error');\n      } else {\n        resolve({ id: userId, name: `User ${userId}` });\n      }\n    }, 1000);\n  });\n};\n\nconst fetchUserEpic: Epic<UserAction, UserAction, RootState> = (action$, state$) => action$.pipe(\n  ofType<UserAction, 'FETCH_USER_REQUEST'>('FETCH_USER_REQUEST'),\n  tap(action => console.log(`[Epic] Caught FETCH_USER_REQUEST for ID: ${action.payload}`)),\n  mergeMap(action =>\n    from(fetchUserApi(action.payload)).pipe( // Convert Promise to Observable\n      map(user => fetchUserSuccess(user)),\n      catchError(error => of(fetchUserFailure(error.toString())))\n    )\n  )\n);\n\n// --- Combine all epics ---\nconst rootEpic = combineEpics(\n  fetchUserEpic\n);\n\n// --- 4. Create Store and run Epic Middleware ---\nconst epicMiddleware = createEpicMiddleware<UserAction, UserAction, RootState>();\n\nconst store = createStore(\n  rootReducer,\n  applyMiddleware(epicMiddleware)\n);\n\nepicMiddleware.run(rootEpic);\n\n// --- 5. Dispatch Actions ---\nconsole.log('Initial state:', store.getState());\n\nstore.dispatch(fetchUserRequest('123'));\n\nsetTimeout(() => {\n  console.log('State after first request:', store.getState());\n  store.dispatch(fetchUserRequest('456'));\n}, 1500);\n\nsetTimeout(() => {\n  console.log('State after second request:', store.getState());\n  store.dispatch(fetchUserRequest('error'));\n}, 3000);\n\nsetTimeout(() => {\n  console.log('Final state:', store.getState());\n}, 4500);\n","lang":"typescript","description":"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.","tag":null,"tag_description":null,"last_tested":null,"results":[]},"compatibility":null}