Skip to content

Commit

Permalink
Warn When dispatching During Middleware Setup (#1485)
Browse files Browse the repository at this point in the history
* Add a doc section on dispatching during middleware setup.

* Warn when dispatching during middleware setup.

* Upgrade the warning to an error.

* Update docs to match thrown error behavior.
  • Loading branch information
timdorr committed Nov 3, 2017
1 parent 0942183 commit c2aeff6
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 2 deletions.
6 changes: 5 additions & 1 deletion docs/advanced/Middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,16 @@ The implementation of [`applyMiddleware()`](../api/applyMiddleware.md) that ship

* It only exposes a subset of the [store API](../api/Store.md) to the middleware: [`dispatch(action)`](../api/Store.md#dispatch) and [`getState()`](../api/Store.md#getState).

* It does a bit of trickery to make sure that if you call `store.dispatch(action)` from your middleware instead of `next(action)`, the action will actually travel the whole middleware chain again, including the current middleware. This is useful for asynchronous middleware, as we have seen [previously](AsyncActions.md).
* It does a bit of trickery to make sure that if you call `store.dispatch(action)` from your middleware instead of `next(action)`, the action will actually travel the whole middleware chain again, including the current middleware. This is useful for asynchronous middleware, as we have seen [previously](AsyncActions.md). There is one caveat when calling `dispatch` during setup, described below.

* To ensure that you may only apply middleware once, it operates on `createStore()` rather than on `store` itself. Instead of `(store, middlewares) => store`, its signature is `(...middlewares) => (createStore) => createStore`.

Because it is cumbersome to apply functions to `createStore()` before using it, `createStore()` accepts an optional last argument to specify such functions.

#### Caveat: Dispatching During Setup

While `applyMiddleware` executes and sets up your middleware, the `store.dispatch` function will point to the vanilla version provided by `createStore`. Dispatching would result in no other middleware being applied. If you are expecting an interaction with another middleware during setup, you will probably be disappointed. Because of this unexpected behavior, `applyMiddleware` will throw an error if you try to dispatch an action before the set up completes. Instead, you should either communicate directly with that other middleware via a common object (for an API-calling middleware, this may be your API client object) or waiting until after the middleware is constructed with a callback.

### The Final Approach

Given this middleware we just wrote:
Expand Down
7 changes: 6 additions & 1 deletion src/applyMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import compose from './compose'
export default function applyMiddleware(...middlewares) {
return (createStore) => (...args) => {
const store = createStore(...args)
let dispatch = store.dispatch
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
let chain = []

const middlewareAPI = {
Expand Down
11 changes: 11 additions & 0 deletions test/applyMiddleware.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ import { addTodo, addTodoAsync, addTodoIfEmpty } from './helpers/actionCreators'
import { thunk } from './helpers/middleware'

describe('applyMiddleware', () => {
it('warns when dispatching during middleware setup', () => {
function dispatchingMiddleware(store) {
store.dispatch(addTodo('Dont dispatch in middleware setup'))
return next => action => next(action)
}

expect(() =>
applyMiddleware(dispatchingMiddleware)(createStore)(reducers.todos)
).toThrow()
})

it('wraps dispatch method with middleware once', () => {
function test(spyOnMethods) {
return methods => {
Expand Down

0 comments on commit c2aeff6

Please sign in to comment.