# Retux Observable
This is a guide on how to strongly type Redux Observable (opens new window) in Retux architecture.
Also see the example project (opens new window).
# Prerequisites
# Directory Structure
Following the standard Retux directory structure, we place epics in a dedicated directory as it runs side by side with the Redux store.
├── src
│ ├── retux-store
│ │ ├── epics
│ │ │ ├── todos.ts
│ │ │ ├── utils.ts
│ │ │ └── index.ts
│ │ ├── modules
│ │ │ ├── todos.ts
│ │ │ ├── visibilityFilter.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ │
# Customize Epic and ofType
Customize the Epic
type and ofType
for easy use in Retux.
// src/retux-store/epics/utils.ts
import { Observable } from 'rxjs'
import { Epic as RawEpic, ofType as rawOfType } from 'redux-observable'
import { StoreAction, StoreActionType, StoreState } from '../modules'
/** Tailored `Epic` for the store. */
export type Epic<
TOutType extends StoreActionType = StoreActionType,
TDeps = any
> = RawEpic<StoreAction, StoreAction<TOutType>, StoreState, TDeps>
/**
* Tailored `ofType` for the store.
* Now you can use `ofType` directly without the need to
* manually offer types each time.
*/
export const ofType = rawOfType as <
TInAction extends StoreAction,
TTypes extends StoreActionType[] = StoreActionType[],
TOutAction extends StoreAction = StoreAction<TTypes[number]>
>(
...types: TTypes
) => (source: Observable<TInAction>) => Observable<TOutAction>
Now you can write type-safe Epics with ease.
import { mapTo, switchMap } from 'rxjs/operators'
import { timer } from 'rxjs'
// Helpers that are tailored for the store
import { Epic, ofType } from './utils'
export const pingEpic: Epic<'PINGPONG$PONG'> = action$ =>
action$.pipe(
ofType('PINGPONG$PING'),
// correctly infer 'PINGPONG$PING' action
switchMap(({ payload: delay = 1000 }) => timer(delay)),
mapTo({ type: 'PINGPONG$PONG' })
)
# Expose Root Epic
Combine Epics.
// src/retux-store/epics/index.ts
import { combineEpics } from 'redux-observable'
import { pingEpic } from './ping'
export const rootEpic = combineEpics(pingEpic)
# Setup Middleware
// src/retux-store/index.ts
import { applyMiddleware, createStore as createReduxStore } from 'redux'
import { createEpicMiddleware } from 'redux-observable'
import { StoreAction, StoreState, rootReducer } from './modules'
import { rootEpic } from './epics'
/** Redux store setup */
export const createStore = () => {
const epicMiddleware = createEpicMiddleware<
StoreAction,
StoreAction,
StoreState
>()
const store = createReduxStore(rootReducer, applyMiddleware(epicMiddleware))
epicMiddleware.run(rootEpic)
return store
}