Reputation: 16534
I'm trying to evaluate redux-observable. Just looking through the doc and I'm trying to get the async epic loading thing going. I created a fork of the jsbin from the docs which basically attempts to add the async usage of the BehaviorSubject stuff.
http://jsbin.com/bazoqemiqu/edit?html,js,output
In that 'PING PONG' example, I added an 'OTHER' action and then use BehaviorSubject.next (as described in the docs) to add that epic. However, when I run the example, what happens is that the PING action is fired, followed by an endless stream of 'OTHER' actions, but never the PONG action. To see this, I added the reduxLogger. View it in the dev tools console as the jsbin console doesn't render it correctly.
My question is what am I doing wrong? Why does the PONG action never get dispatched?
Upvotes: 0
Views: 1210
Reputation: 15401
otherEpic
is an infinite "loop" (over time)const otherEpic$ = action$ =>
action$
.delay(1000)
.mapTo({ type: OTHER });
This epic has the behavior "when any action at all is received, wait 1000ms and then emit another action of type OTHER
". And since the actions your Epics emit go through the normal store.dispatch
cycle like any other action, that means after the first PING
is received, it will emit an OTHER
after 1000ms, which will then be recursively received by the same epic again, wait another 1000ms and emit another OTHER
, repeat forever.
I'm not sure if this was known, but wanted to point it out.
next()
into the BehaviorSubject of epic$
before your rootEpic has started running/been subscribed to it.BehaviorSubjects will keep the last value emitted and provide that immediately when someone subscribes. Since your rootEpic has not yet been called and subscribed to the by the middleware, you're replacing the initial value, so only the otherEpic
is emitted and ran through the epic$.mergeMap
stuff.
In a real application with async/bundle splitting, when you would call epic$.next(newEpic)
should always be after the middleware has subscribed to your rootEpic and received the initial epic you provided to your BehaviorSubject
.
Here's a demo of that working: http://jsbin.com/zaniviz/edit?js,output
const epic$ = new BehaviorSubject(combineEpics(epic1, epic2, ...etc));
const rootEpic = (action$, store) =>
epic$.mergeMap(epic => {console.log(epic)
return epic(action$, store)
});
const otherEpic = action$ =>
action$.ofType(PONG)
.delay(1000)
.mapTo({ type: OTHER });
const epicMiddleware = createEpicMiddleware(rootEpic);
const store = createStore(rootReducer,
applyMiddleware(loggerMiddleware, epicMiddleware)
);
// any time AFTER the epicMiddleware
// has received the rootEpic
epic$.next(otherEpic);
The documentation says "sometime later" in the example, which I now see isn't clear enough. I'll try and clarify this further.
You may also find this other question on async loading of Epics useful if you're using react-router with Webpack's require.enquire()
splitting.
Let me know if I can clarify any of these further 🖖
Upvotes: 4