{"action":"created","issue":{"url":"https://api.github.com/repos/rackt/redux/issues/1171","labels_url":"https://api.github.com/repos/rackt/redux/issues/1171/labels{/name}","comments_url":"https://api.github.com/repos/rackt/redux/issues/1171/comments","events_url":"https://api.github.com/repos/rackt/redux/issues/1171/events","html_url":"https://github.com/rackt/redux/issues/1171","id":123565061,"number":1171,"title":"Recommendations for best practices regarding action-creators, reducers, and selectors","user":{"login":"bvaughn","id":29597,"avatar_url":"https://avatars.githubusercontent.com/u/29597?v=3","gravatar_id":"","url":"https://api.github.com/users/bvaughn","html_url":"https://github.com/bvaughn","followers_url":"https://api.github.com/users/bvaughn/followers","following_url":"https://api.github.com/users/bvaughn/following{/other_user}","gists_url":"https://api.github.com/users/bvaughn/gists{/gist_id}","starred_url":"https://api.github.com/users/bvaughn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bvaughn/subscriptions","organizations_url":"https://api.github.com/users/bvaughn/orgs","repos_url":"https://api.github.com/users/bvaughn/repos","events_url":"https://api.github.com/users/bvaughn/events{/privacy}","received_events_url":"https://api.github.com/users/bvaughn/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/rackt/redux/labels/discussion","name":"discussion","color":"fbca04"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-12-22T22:07:21Z","updated_at":"2015-12-23T05:06:11Z","closed_at":null,"body":"My team has been using Redux for a couple of months now. Along the way I've occasionally found myself thinking about a feature and wondering \"does this belong in an action-creator or a reducer?\". The documentation seems a bit vague on this fact. (Or perhaps I've just missed where it's covered, in which case I apologize.) But as I've written more code and more tests I've come to have stronger opinions about where things _should_ be and I thought it would be worth sharing and discussing with others.\r\n\r\nSo here are my thoughts.\r\n\r\n# Use selectors everywhere\r\n\r\nThis first one is not strictly related to Redux but I'll share it anyway since it's indirectly mentioned below. My team uses [rackt/reselect](https://github.com/rackt/reselect). We typically define a file that exports selectors for a given node of our state tree (eg. MyPageSelectors). Our \"smart\" containers then use those selectors to parameterize our \"dumb\" components.\r\n\r\nOver time we've realized that there is added benefit to using these same selectors in other places (not just in the context of reselect). For example, we use them in automated tests. We also use them in [thunks](https://github.com/gaearon/redux-thunk) returned by action-creators (more below).\r\n\r\nSo my first recommendation is- use shared selectors _everywhere_- even when synchronously accessing data (eg. prefer `myValueSelector(state)` over `state.myValue`). This reduces the likelihood of mistyped variables that lead to subtle undefined values, it simplifies changes to the structure of your store, etc.\r\n\r\n# Do _more_ in action-creators and _less_ in reducers\r\n\r\nI think this one is very important although it may not be immediately obvious. Business logic belongs in action-creators. Reducers should be stupid and simple. In many individual cases it does not matter- but consistency is good and so it's best to _consistently_ do this. There are a couple of reasons why:\r\n 1. Action-creators can be asynchronous through the use of middleware like [redux-thunk](https://github.com/gaearon/redux-thunk). Since your application will often require asynchronous updates to your store- some \"business logic\" will end up in your actions.\r\n 1. Action-creators (more accurately the [thunks](https://github.com/gaearon/redux-thunk) they return) can use shared selectors because they have access to the complete state. Reducers cannot because they only have access to their node.\r\n 1. Using [redux-thunk](https://github.com/gaearon/redux-thunk), a single action-creator can dispatch multiple actions- which makes complicated state updates simpler and encourages better code reuse.\r\n\r\nImagine your state has metadata related to a list of items. Each time an item is modified, added to, or removed from the list- the metadata needs to be updated. The \"business logic\" for keeping the list and its metadata in sync could live in a few places:\r\n 1. In the reducers. Each reducer (add, edit, remove) is responsible for updating the list _as well as_ the metadata.\r\n 1. In the views (container/component). Each view that invokes an action (add, edit, remove) it is also responsible for invoking an `updateMetadata` action. This approach is terrible for (hopefully) obvious reasons.\r\n 1. In the action-creators. Each action-creator (add, edit, remove) returns a [thunk](https://github.com/gaearon/redux-thunk) that dispatches an action to update the list and then another action to updates the metadata.\r\n\r\nGiven the above choices, option 3 is solidly better. Both options 1 and 3 support clean code sharing but only option 3 supports the case where list and/or metadata updates might be asynchronous. (For example maybe it relies on a web worker.)\r\n\r\n# Write \"ducks\" tests that focus on Actions and Selectors\r\n\r\nThe most efficient way to tests actions, reducers, and selectors is to follow the [\"ducks\"](https://github.com/erikras/ducks-modular-redux) approach when writing tests. This means you should write one set of tests that cover a given set of actions, reducers, and selectors rather than 3 sets of tests that focus on each individually. This more accurately simulates what happens in your real application and it provides the most bang for the buck.\r\n\r\nBreaking it down further I've found that it's useful to write tests that focus on action-creators and then verify the outcome using selectors. (Don't directly test reducers.) What matters is that a given action results in the state you expect. Verifying this outcome using your (shared) selectors is a way of covering all three in a single pass."},"comment":{"url":"https://api.github.com/repos/rackt/redux/issues/comments/166804318","html_url":"https://github.com/rackt/redux/issues/1171#issuecomment-166804318","issue_url":"https://api.github.com/repos/rackt/redux/issues/1171","id":166804318,"user":{"login":"cpsubrian","id":319152,"avatar_url":"https://avatars.githubusercontent.com/u/319152?v=3","gravatar_id":"","url":"https://api.github.com/users/cpsubrian","html_url":"https://github.com/cpsubrian","followers_url":"https://api.github.com/users/cpsubrian/followers","following_url":"https://api.github.com/users/cpsubrian/following{/other_user}","gists_url":"https://api.github.com/users/cpsubrian/gists{/gist_id}","starred_url":"https://api.github.com/users/cpsubrian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cpsubrian/subscriptions","organizations_url":"https://api.github.com/users/cpsubrian/orgs","repos_url":"https://api.github.com/users/cpsubrian/repos","events_url":"https://api.github.com/users/cpsubrian/events{/privacy}","received_events_url":"https://api.github.com/users/cpsubrian/received_events","type":"User","site_admin":false},"created_at":"2015-12-23T05:06:11Z","updated_at":"2015-12-23T05:06:11Z","body":"Curious if you use Immutable.js or other. In the handful of redux things I've built I couldn't imagine **not** using immutable, but I *do* have a pretty deeply nested structure that Immutable helps tame."}}