Permalink
Browse files

feat(epics): calling `store.dispatch()` directly inside your epics is…

… now deprecated and will be removed in v1.0.0 (#336)

Instead of using `store.dispatch()` directly, your epics should emit actions you wish to dispatch through the Observable you return. Actions in, actions out. `store.dispatch()` was made available as an escape hatch, but in practice it has been a footgun for many users. It will be removed in v1.0.0
  • Loading branch information...
evertbouw authored and jayphelps committed Oct 11, 2017
1 parent 434e521 commit 76ecd338bf1c0a7bed3120fe883d27ec613d1e51
Showing with 35 additions and 8 deletions.
  1. +15 −7 src/createEpicMiddleware.js
  2. +20 −1 test/createEpicMiddleware-spec.js
@@ -13,8 +13,8 @@ const defaultOptions = {
adapter: defaultAdapter
};
export function createEpicMiddleware(epic, options = defaultOptions) {
if (typeof epic !== 'function') {
export function createEpicMiddleware(rootEpic, options = defaultOptions) {
if (typeof rootEpic !== 'function') {
throw new TypeError('You must provide a root Epic to createEpicMiddleware');
}
@@ -34,9 +34,17 @@ export function createEpicMiddleware(epic, options = defaultOptions) {
return next => {
epic$
::map(epic => {
const vault = (process.env.NODE_ENV === 'production') ? store : {
getState: store.getState,
dispatch: (action) => {
console.warn(`Your Epic "${epic.name || '<anonymous>'}" called store.dispatch directly. This is an anti-pattern.`);
return store.dispatch(action);
}
};
const output$ = ('dependencies' in options)
? epic(action$, store, options.dependencies)
: epic(action$, store);
? epic(action$, vault, options.dependencies)
: epic(action$, vault);
if (!output$) {
throw new TypeError(`Your root Epic "${epic.name || '<anonymous>'}" does not return a stream. Double check you\'re not missing a return statement!`);
@@ -48,7 +56,7 @@ export function createEpicMiddleware(epic, options = defaultOptions) {
.subscribe(store.dispatch);
// Setup initial root epic
epic$.next(epic);
epic$.next(rootEpic);
return action => {
const result = next(action);
@@ -58,13 +66,13 @@ export function createEpicMiddleware(epic, options = defaultOptions) {
};
};
epicMiddleware.replaceEpic = epic => {
epicMiddleware.replaceEpic = rootEpic => {
// gives the previous root Epic a last chance
// to do some clean up
store.dispatch({ type: EPIC_END });
// switches to the new root Epic, synchronously terminating
// the previous one
epic$.next(epic);
epic$.next(rootEpic);
};
return epicMiddleware;
@@ -11,6 +11,8 @@ import { of } from 'rxjs/observable/of';
import { empty } from 'rxjs/observable/empty';
import { mergeStatic } from 'rxjs/operator/merge';
import { mapTo } from 'rxjs/operator/mapTo';
import { map } from 'rxjs/operator/map';
import { ignoreElements } from 'rxjs/operator/ignoreElements';
describe('createEpicMiddleware', () => {
it('should provide epics a stream of action$ in and the "lite" store', (done) => {
@@ -20,13 +22,30 @@ describe('createEpicMiddleware', () => {
const mockMiddleware = store => next => action => {
expect(epic.calledOnce).to.equal(true);
expect(epic.firstCall.args[0]).to.be.instanceOf(ActionsObservable);
expect(epic.firstCall.args[1]).to.equal(store);
expect(epic.firstCall.args[1].getState).to.equal(store.getState);
done();
};
const store = createStore(reducer, applyMiddleware(epicMiddleware, mockMiddleware));
store.dispatch({ type: 'FIRST_ACTION_TO_TRIGGER_MIDDLEWARE' });
});
it('should warn about improper use of dispatch function', () => {
sinon.spy(console, 'warn');
const reducer = (state = [], action) => state.concat(action);
const epic = (action$, store) => action$
.ofType('PING')
::map(() => store.dispatch({ type: 'PONG' }))
::ignoreElements();
const middleware = createEpicMiddleware(epic);
const store = createStore(reducer, applyMiddleware(middleware));
store.dispatch({ type: 'PING' });
expect(console.warn.callCount).to.equal(1);
console.warn.restore();
});
it('should accept an epic that wires up action$ input to action$ out', () => {
const reducer = (state = [], action) => state.concat(action);
const epic = (action$, store) =>

0 comments on commit 76ecd33

Please sign in to comment.