10
votes

I work on a React Native app using redux-observable. I have 10+ epics, all ending with .catch(err => console.error(err)) in order to display the React Native "Red box" (see https://facebook.github.io/react-native/docs/debugging.html#errors) in case of errors inside the epic.

How can I define a global error handler instead, used by all epics (they are combined later by the combineEpics function) ?

2

2 Answers

12
votes

redux-observable leans on idiomatic paradigms of RxJS for nearly everything you would do. When you combine Epics together, you're creating a new Epic that has the normal function (action$, store) signature. So this new Epic represents everything output from those individual Epics, including errors.

You can leverage that fact by defining your own rootEpic and composing the result of your combineEpics() call. Since Epics are factories, you'll need to remember to pass along (action$, store)!

Here's one way:

export const rootEpic = (action$, store) =>
  combineEpics(epic1, epic2, epic3)(action$, store) // <-- call epic w/ args
    .do({ error: err => console.error(err) });

You probably shouldn't use the .catch() operator for this, because .catch() is for returning some other Observable to use instead, when there is an error. If you just want to log the error out (and not try to recover), .do() is the ideal operator--its purpose is to allow you to do external side effects (like logging) that have no effect on the stream itself.

Composing Epics is very powerful, not just in this case, but many others that we're planning to describe soon.

Related to the .catch() operator, you may also be interested in the RFC for better default error handling.

2
votes

2020 update

import { concat, of } from 'rxjs';
import { combineEpics } from 'redux-observable';
import { catchError } from 'rxjs/operators';

export const rootEpic = (action$, store) =>
  combineEpics(epic1, epic2, epic3)(action$, store)
    .pipe((error, caught) => concat(caught));
import { createEpicMiddleware } from 'redux-observable';
const epicMiddleware = createEpicMiddleware();
epicMiddleware.run((action$, store) => rootEpics(action$, store));

You can additionally pass redux error action like that:

concat(of(reduxActionForError), caught));