Setting Up Redux Observable
If you are adding redux observable to a new redux project, the first step is to install it along with RXJS
npm i redux-observable rxjs
The next step is to set up the middleware.
Create a middleware and pass it to the createStore
function from Redux. Then you create a root epic that combines all your epics and call epicMiddleware.run()
with the rootEpic
.
import { createStore, compose, applyMiddleware } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
import { combineEpics } from 'redux-observable';
import { epicA } from './epicA';
import { epicB } from './epicB';
export const rootEpic = combineEpics(
epicA,
epicB
);
const epicMiddleware = createEpicMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default function configureStore() {
const store = createStore(
rootReducer,
composeEnhancers(
applyMiddleware(epicMiddleware)
)
);
epicMiddleware.run(rootEpic);
return store;
}
If you are using redux Tooljit then you can replace the configureStore
function with:
import { configureStore } from '@reduxjs/toolkit';
export default configureStore( {reducer: rootReducer, middleware: [epicMiddleware] } );
epicMiddleware.run(rootEpic);
export default configureStore( {reducer: rootReducer, middleware: [epicMiddleware] } );
epicMiddleware.run(rootEpic);
Now any epic you create will fire as soon as the app boots.
To call epics on demand, you filter then out of the action stream using `ofType`. So if you want to trigger the following epic then fire an action named `example/epicA`.
import {ofType} from “redux-observable”;
export const someEpic = action$ => action$.pipe(
ofType(‘example/epicA’),
delay(1000),
mapTo({ type: ‘example/epicB’ })
);
The last thing to consider is adding a global error handler to the root epic to catch bugs.
const rootEpic = (action$, store$, dependencies) =>
combineEpics(…epics)(action$, store$, dependencies).pipe(
catchError((error, source) => {
console.error(error);
return source;
})
);
# Resources
- [The official docs](https://redux-observable.js.org/docs/basics/SettingUpTheMiddleware.html) on setting up middleware.
- Redux now recommends writing actions as `domain/eventName` rather than `SCREAMING_SNAKE_CASE`. Don't believe me? [here you go](https://redux.js.org/style-guide/style-guide#write-action-types-as-domain-eventname).
Example Final Store.js for redux toolkit boilerplate
import { configureStore } from ‘@reduxjs/toolkit’;
import counterReducer from ‘../features/counter/counterSlice’;
import { createEpicMiddleware } from ‘redux-observable’;
import { combineEpics } from ‘redux-observable’;
import { combineReducers } from ‘redux’;
import { counterEpic } from ‘../features/counter/counterEpic’;
import {catchError} from ‘rxjs/operators’
export const epics = [
counterEpic
]
export const rootEpic = (action$, store$, dependencies) =>
combineEpics(…epics)(action$).pipe(
catchError((error, source) => {
console.error(error);
return source;
})
);
export const rootReducer = combineReducers({
counter: counterReducer
});
const epicMiddleware = createEpicMiddleware();
export default configureStore( {reducer: rootReducer, middleware: [epicMiddleware] } );
epicMiddleware.run(rootEpic);