{"action":"closed","number":213,"pull_request":{"url":"https://api.github.com/repos/gaearon/redux/pulls/213","id":39211831,"html_url":"https://github.com/gaearon/redux/pull/213","diff_url":"https://github.com/gaearon/redux/pull/213.diff","patch_url":"https://github.com/gaearon/redux/pull/213.patch","issue_url":"https://api.github.com/repos/gaearon/redux/issues/213","number":213,"state":"closed","locked":false,"title":"Update middleware api","user":{"login":"acdlite","id":3624098,"avatar_url":"https://avatars.githubusercontent.com/u/3624098?v=3","gravatar_id":"","url":"https://api.github.com/users/acdlite","html_url":"https://github.com/acdlite","followers_url":"https://api.github.com/users/acdlite/followers","following_url":"https://api.github.com/users/acdlite/following{/other_user}","gists_url":"https://api.github.com/users/acdlite/gists{/gist_id}","starred_url":"https://api.github.com/users/acdlite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/acdlite/subscriptions","organizations_url":"https://api.github.com/users/acdlite/orgs","repos_url":"https://api.github.com/users/acdlite/repos","events_url":"https://api.github.com/users/acdlite/events{/privacy}","received_events_url":"https://api.github.com/users/acdlite/received_events","type":"User","site_admin":false},"body":"This PR updates the middleware API in several ways.\r\n\r\n## All middleware is now smart middleware\r\n\r\nThe API becomes much simpler if we just assume that all middleware is \"smart\" middleware. I know previously I argued against something like this:\r\n\r\n```js\r\n(dispatch, getState, next) => action => {...}\r\n```\r\n\r\nbecause it's fragile, hard to compose, and awkward to use from the call site. I also figured most middleware wouldn't need either `dispatch()` or `getState()`. But as we discovered with the thunk middleware, any asynchronous middleware is going to need `dispatch()` so that it can dispatch from the beginning of the chain. So let's just make all middleware \"smart\" like this:\r\n\r\n```js\r\nmethods => next => action {...}\r\n```\r\n\r\nwhere `methods` is a subset of the store, currently `{ dispatch, getState }`. Now it's easy to compose using just function composition. And if users have already written their own \"dumb\" middleware, all they need to do to update is wrap it in one more function:\r\n\r\n```js\r\nconst smartMiddleware = () => dumbMiddleware;\r\n```\r\n\r\n## `applyMiddleware(...middlewares)`\r\n\r\nNow that all middleware is smart middleware, we don't have to deal with [this nonsense](https://github.com/gaearon/redux/pull/195#issuecomment-117937929). We get a nice, simple API for applying middleware:\r\n\r\n```js\r\nconst finalCreateStore = applyMiddleware(thunk, promise, observable)(createStore);\r\n```\r\n\r\n`applyMiddleware()` is a higher-order store, just like @gaearon's devtools (I assume... haven't seen the final version yet.). So, it composes!\r\n\r\n```js\r\nconst finalCreateStore = compose(\r\n  applyMiddleware(thunk, promise, observable)\r\n  devtools,\r\n  createStore\r\n);\r\n```\r\n\r\n## Caveat: `thunkMiddleware()` is no longer built-in to `createStore()`\r\n\r\nThis is because middleware needs to come before any other higher-order stores, since middleware has the potential to be asynchronous.\r\n\r\nHowever! It is still the default when using `applyMiddleware()`. It's additional step versus today, but I imagine most users will be using `applyMiddleware`, anyway:\r\n\r\n```js\r\nconst finalCreateStore = applyMiddleware()(createStore);\r\n\r\n// Same as\r\nconst finalCreateStore = applyMiddleware(thunk)(createStore);\r\n```\r\n\r\n### Updated `composeMiddleware()`\r\n\r\nI updated `composeMiddleware()` to support the new signature. In the process, I also discovered a bug with the previous implementation where it only works if dispatch is the last argument. It did not combine multiple middlewares into a single middlewares as advertised; it only combined multiple middlewares with dispatch to create a new dispatch.\r\n\r\nThe reason is because it was simply doing function composition, where `compose(a, b, c)` becomes `a(b(c))`. But this isn't right, because this\r\n\r\n```js\r\nm1(m2(m3(dispatch))\r\n```\r\n\r\nis not the same this\r\n\r\n```js\r\nm1(m2(m3))(dispatch)\r\n```\r\n\r\nWhich means previously we couldn't do this, as advertised in the docs (and by, ya know, the name of the function):\r\n\r\n```js\r\nconst middleware = composeMiddleware(m1, m2, m3);\r\nconst finalDispatch = middleware(dispatch);\r\n```\r\n\r\nI fixed the issue and updated the test. We'll need to update the docs in master, too, as they're currently incorrect.\r\n\r\nI've renamed the previous method to just `compose()`, since it's still useful elsewhere.","created_at":"2015-07-04T19:12:53Z","updated_at":"2015-07-08T11:23:10Z","closed_at":"2015-07-08T11:23:10Z","merged_at":"2015-07-08T11:23:10Z","merge_commit_sha":"98d3e494f06e8578d63767745578405ac68153ed","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/gaearon/redux/pulls/213/commits","review_comments_url":"https://api.github.com/repos/gaearon/redux/pulls/213/comments","review_comment_url":"https://api.github.com/repos/gaearon/redux/pulls/comments{/number}","comments_url":"https://api.github.com/repos/gaearon/redux/issues/213/comments","statuses_url":"https://api.github.com/repos/gaearon/redux/statuses/b72f90960314d4e95f13f2329429edfdfe0f1849","head":{"label":"gaearon:update-middleware-api","ref":"update-middleware-api","sha":"b72f90960314d4e95f13f2329429edfdfe0f1849","user":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"repo":{"id":36535156,"name":"redux","full_name":"gaearon/redux","owner":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gaearon/redux","description":"Atomic Flux with hot reloading","fork":false,"url":"https://api.github.com/repos/gaearon/redux","forks_url":"https://api.github.com/repos/gaearon/redux/forks","keys_url":"https://api.github.com/repos/gaearon/redux/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gaearon/redux/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gaearon/redux/teams","hooks_url":"https://api.github.com/repos/gaearon/redux/hooks","issue_events_url":"https://api.github.com/repos/gaearon/redux/issues/events{/number}","events_url":"https://api.github.com/repos/gaearon/redux/events","assignees_url":"https://api.github.com/repos/gaearon/redux/assignees{/user}","branches_url":"https://api.github.com/repos/gaearon/redux/branches{/branch}","tags_url":"https://api.github.com/repos/gaearon/redux/tags","blobs_url":"https://api.github.com/repos/gaearon/redux/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gaearon/redux/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gaearon/redux/git/refs{/sha}","trees_url":"https://api.github.com/repos/gaearon/redux/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gaearon/redux/statuses/{sha}","languages_url":"https://api.github.com/repos/gaearon/redux/languages","stargazers_url":"https://api.github.com/repos/gaearon/redux/stargazers","contributors_url":"https://api.github.com/repos/gaearon/redux/contributors","subscribers_url":"https://api.github.com/repos/gaearon/redux/subscribers","subscription_url":"https://api.github.com/repos/gaearon/redux/subscription","commits_url":"https://api.github.com/repos/gaearon/redux/commits{/sha}","git_commits_url":"https://api.github.com/repos/gaearon/redux/git/commits{/sha}","comments_url":"https://api.github.com/repos/gaearon/redux/comments{/number}","issue_comment_url":"https://api.github.com/repos/gaearon/redux/issues/comments{/number}","contents_url":"https://api.github.com/repos/gaearon/redux/contents/{+path}","compare_url":"https://api.github.com/repos/gaearon/redux/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gaearon/redux/merges","archive_url":"https://api.github.com/repos/gaearon/redux/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gaearon/redux/downloads","issues_url":"https://api.github.com/repos/gaearon/redux/issues{/number}","pulls_url":"https://api.github.com/repos/gaearon/redux/pulls{/number}","milestones_url":"https://api.github.com/repos/gaearon/redux/milestones{/number}","notifications_url":"https://api.github.com/repos/gaearon/redux/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gaearon/redux/labels{/name}","releases_url":"https://api.github.com/repos/gaearon/redux/releases{/id}","created_at":"2015-05-29T23:53:15Z","updated_at":"2015-07-08T09:57:05Z","pushed_at":"2015-07-08T11:23:10Z","git_url":"git://github.com/gaearon/redux.git","ssh_url":"git@github.com:gaearon/redux.git","clone_url":"https://github.com/gaearon/redux.git","svn_url":"https://github.com/gaearon/redux","homepage":"https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31","size":2603,"stargazers_count":1945,"watchers_count":1945,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":92,"mirror_url":null,"open_issues_count":39,"forks":92,"open_issues":39,"watchers":1945,"default_branch":"master"}},"base":{"label":"gaearon:breaking-changes-1.0","ref":"breaking-changes-1.0","sha":"069d6414bd1828a0d8002d94dc0eddfcb7e43768","user":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"repo":{"id":36535156,"name":"redux","full_name":"gaearon/redux","owner":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gaearon/redux","description":"Atomic Flux with hot reloading","fork":false,"url":"https://api.github.com/repos/gaearon/redux","forks_url":"https://api.github.com/repos/gaearon/redux/forks","keys_url":"https://api.github.com/repos/gaearon/redux/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gaearon/redux/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gaearon/redux/teams","hooks_url":"https://api.github.com/repos/gaearon/redux/hooks","issue_events_url":"https://api.github.com/repos/gaearon/redux/issues/events{/number}","events_url":"https://api.github.com/repos/gaearon/redux/events","assignees_url":"https://api.github.com/repos/gaearon/redux/assignees{/user}","branches_url":"https://api.github.com/repos/gaearon/redux/branches{/branch}","tags_url":"https://api.github.com/repos/gaearon/redux/tags","blobs_url":"https://api.github.com/repos/gaearon/redux/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gaearon/redux/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gaearon/redux/git/refs{/sha}","trees_url":"https://api.github.com/repos/gaearon/redux/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gaearon/redux/statuses/{sha}","languages_url":"https://api.github.com/repos/gaearon/redux/languages","stargazers_url":"https://api.github.com/repos/gaearon/redux/stargazers","contributors_url":"https://api.github.com/repos/gaearon/redux/contributors","subscribers_url":"https://api.github.com/repos/gaearon/redux/subscribers","subscription_url":"https://api.github.com/repos/gaearon/redux/subscription","commits_url":"https://api.github.com/repos/gaearon/redux/commits{/sha}","git_commits_url":"https://api.github.com/repos/gaearon/redux/git/commits{/sha}","comments_url":"https://api.github.com/repos/gaearon/redux/comments{/number}","issue_comment_url":"https://api.github.com/repos/gaearon/redux/issues/comments{/number}","contents_url":"https://api.github.com/repos/gaearon/redux/contents/{+path}","compare_url":"https://api.github.com/repos/gaearon/redux/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gaearon/redux/merges","archive_url":"https://api.github.com/repos/gaearon/redux/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gaearon/redux/downloads","issues_url":"https://api.github.com/repos/gaearon/redux/issues{/number}","pulls_url":"https://api.github.com/repos/gaearon/redux/pulls{/number}","milestones_url":"https://api.github.com/repos/gaearon/redux/milestones{/number}","notifications_url":"https://api.github.com/repos/gaearon/redux/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gaearon/redux/labels{/name}","releases_url":"https://api.github.com/repos/gaearon/redux/releases{/id}","created_at":"2015-05-29T23:53:15Z","updated_at":"2015-07-08T09:57:05Z","pushed_at":"2015-07-08T11:23:10Z","git_url":"git://github.com/gaearon/redux.git","ssh_url":"git@github.com:gaearon/redux.git","clone_url":"https://github.com/gaearon/redux.git","svn_url":"https://github.com/gaearon/redux","homepage":"https://medium.com/@dan_abramov/the-evolution-of-flux-frameworks-6c16ad26bb31","size":2603,"stargazers_count":1945,"watchers_count":1945,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":92,"mirror_url":null,"open_issues_count":39,"forks":92,"open_issues":39,"watchers":1945,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/gaearon/redux/pulls/213"},"html":{"href":"https://github.com/gaearon/redux/pull/213"},"issue":{"href":"https://api.github.com/repos/gaearon/redux/issues/213"},"comments":{"href":"https://api.github.com/repos/gaearon/redux/issues/213/comments"},"review_comments":{"href":"https://api.github.com/repos/gaearon/redux/pulls/213/comments"},"review_comment":{"href":"https://api.github.com/repos/gaearon/redux/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/gaearon/redux/pulls/213/commits"},"statuses":{"href":"https://api.github.com/repos/gaearon/redux/statuses/b72f90960314d4e95f13f2329429edfdfe0f1849"}},"merged":true,"mergeable":null,"mergeable_state":"unknown","merged_by":{"login":"gaearon","id":810438,"avatar_url":"https://avatars.githubusercontent.com/u/810438?v=3","gravatar_id":"","url":"https://api.github.com/users/gaearon","html_url":"https://github.com/gaearon","followers_url":"https://api.github.com/users/gaearon/followers","following_url":"https://api.github.com/users/gaearon/following{/other_user}","gists_url":"https://api.github.com/users/gaearon/gists{/gist_id}","starred_url":"https://api.github.com/users/gaearon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gaearon/subscriptions","organizations_url":"https://api.github.com/users/gaearon/orgs","repos_url":"https://api.github.com/users/gaearon/repos","events_url":"https://api.github.com/users/gaearon/events{/privacy}","received_events_url":"https://api.github.com/users/gaearon/received_events","type":"User","site_admin":false},"comments":1,"review_comments":2,"commits":3,"additions":147,"deletions":112,"changed_files":10}}