-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/erikras/react-redux-unive…
…rsal-hot-example into mocha-play
- Loading branch information
Showing
52 changed files
with
1,026 additions
and
660 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Ducks: Redux Reducer Bundles | ||
|
||
<img src="duck.jpg" align="right"/> | ||
|
||
I find as I am building my redux app, one piece of functionality at a time, I keep needing to add `{actionTypes, actions, reducer}` tuples for each use case. I have been keeping these in separate files and even separate folders, however 95% of the time, it's only one reducer/actions pair that ever needs their associated actions. | ||
|
||
To me, it makes more sense for these components to be bundled together in an isolated module that is self contained, and can even be packaged easily into a library. | ||
|
||
## The Proposal | ||
|
||
### Example | ||
|
||
```javascript | ||
// widgets.js | ||
|
||
const LOAD = 'my-app/widgets/LOAD'; | ||
const CREATE = 'my-app/widgets/CREATE'; | ||
const UPDATE = 'my-app/widgets/UPDATE'; | ||
const REMOVE = 'my-app/widgets/REMOVE'; | ||
|
||
export default function reducer(state = {}, action = {}) { | ||
switch (action.type) { | ||
// do reducer stuff | ||
default: return state; | ||
} | ||
} | ||
|
||
export function loadWidgets() = { | ||
return { type: LOAD }; | ||
} | ||
|
||
export function createWidget(widget) = { | ||
return { type: CREATE, widget }; | ||
} | ||
|
||
export function updateWidget(widget) = { | ||
return { type: UPDATE, widget }; | ||
} | ||
|
||
export function removeWidget(widget) = { | ||
return { type: REMOVE, widget }; | ||
} | ||
``` | ||
### Rules | ||
|
||
A module... | ||
|
||
1. MUST `export default` a function called `reducer()` | ||
2. MUST `export` its action creators as functions | ||
3. MUST have action types in the form `npm-module-or-app/reducer/ACTION_TYPE` | ||
3. MAY export its action types as `UPPER_SNAKE_CASE`, if an external reducer needs to listen for them, or if it is a published reusable library | ||
|
||
These same guidelines are recommended for `{actionType, action, reducer}` bundles that are shared as reusable Redux libraries. | ||
|
||
### Name | ||
|
||
Java has jars and beans. Ruby has gems. I suggest we call these reducer bundles "ducks", as in the last syllable of "redux". | ||
|
||
### Usage | ||
|
||
You can still do: | ||
|
||
```javascript | ||
import { combineReducers } from 'redux'; | ||
import * as reducers from './ducks/index'; | ||
|
||
const rootReducer = combineReducers(reducers); | ||
export default rootReducer; | ||
``` | ||
|
||
You can still do: | ||
|
||
```javascript | ||
import * as widgetActions from './ducks/widgets'; | ||
``` | ||
...and it will only import the action creators, ready to be passed to `bindActionCreators()`. | ||
|
||
There will be some times when you want to `export` something other than an action creator. That's okay, too. The rules don't say that you can *only* `export` action creators. When that happens, you'll just have to enumerate the action creators that you want. Not a big deal. | ||
|
||
```javascript | ||
import {create, update, remove, increment} as widgetActions from './ducks/widgets'; | ||
// ... | ||
bindActionCreators({create, update, remove, increment}, dispatch); | ||
``` | ||
|
||
### Implementation | ||
|
||
The migration to this code structure was [painless](https://github.com/erikras/react-redux-universal-hot-example/commit/3fdf194683abb7c40f3cb7969fd1f8aa6a4f9c57), and I foresee it reducing much future development misery. | ||
|
||
Please submit any feedback via an issue or a tweet to [@erikras](https://twitter.com/erikras). It will be much appreciated. | ||
|
||
Happy coding! | ||
|
||
-- Erik Rasmussen |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
"description": "Example of an isomorphic (universal) webapp using react redux and hot reloading", | ||
"author": "Erik Rasmussen <[email protected]> (http://github.com/erikras)", | ||
"license": "MIT", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/erikras/react-redux-universal-hot-example" | ||
|
@@ -57,64 +57,67 @@ | |
} | ||
}, | ||
"dependencies": { | ||
"babel": "5.4.7", | ||
"babel-plugin-typecheck": "0.0.3", | ||
"babel": "5.8.21", | ||
"babel-plugin-typecheck": "1.2.0", | ||
"body-parser": "^1.13.2", | ||
"compression": "^1.5.0", | ||
"express": "^4.13.0", | ||
"express-session": "^1.11.3", | ||
"file-loader": "^0.8.4", | ||
"http-proxy": "^1.11.1", | ||
"lru-memoize": "0.0.1", | ||
"piping": "0.1.8", | ||
"lru-memoize": "0.0.2", | ||
"map-props": "^0.1.1", | ||
"piping": "0.2.0", | ||
"pretty-error": "^1.1.2", | ||
"query-string": "^2.4.0", | ||
"react": "0.13.3", | ||
"react-inline-css": "1.1.1", | ||
"react-redux": "0.8.0", | ||
"react-router": "v1.0.0-beta2", | ||
"redux": "1.0.0-rc", | ||
"redux-form": "^0.1.2", | ||
"react-document-meta": "^0.1.4", | ||
"react-inline-css": "1.2.1", | ||
"react-redux": "0.9.0", | ||
"react-router": "v1.0.0-beta3", | ||
"redux": "^1.0.1", | ||
"redux-form": "^0.5.0", | ||
"serialize-javascript": "^1.0.0", | ||
"serve-favicon": "^2.3.0", | ||
"serve-static": "^1.10.0", | ||
"superagent": "^1.2.0", | ||
"url-loader": "^0.5.6", | ||
"webpack-isomorphic-tools": "^0.3.3" | ||
"webpack-isomorphic-tools": "^0.8.1" | ||
}, | ||
"devDependencies": { | ||
"redux-devtools": "1.0.2", | ||
"autoprefixer-loader": "^2.0.0", | ||
"babel-core": "5.4.7", | ||
"babel-eslint": "^3.1.18", | ||
"babel-loader": "5.1.3", | ||
"babel-runtime": "5.4.7", | ||
"babel-core": "^5.8.22", | ||
"babel-eslint": "^4.0.10", | ||
"babel-loader": "5.3.2", | ||
"babel-runtime": "5.8.20", | ||
"better-npm-run": "0.0.1", | ||
"chai": "^3.2.0", | ||
"clean-webpack-plugin": "^0.1.3", | ||
"concurrently": "0.1.1", | ||
"css-loader": "^0.15.1", | ||
"eslint": "^0.23.0", | ||
"eslint-config-airbnb": "0.0.6", | ||
"eslint-plugin-react": "^2.5.2", | ||
"css-loader": "^0.16.0", | ||
"eslint": "^1.2.0", | ||
"eslint-config-airbnb": "0.0.7", | ||
"eslint-plugin-react": "^3.2.3", | ||
"extract-text-webpack-plugin": "^0.8.1", | ||
"json-loader": "0.5.2", | ||
"karma": "^0.13.3", | ||
"karma-chrome-launcher": "^0.2.0", | ||
"karma-cli": "0.0.4", | ||
"karma-cli": "0.1.0", | ||
"karma-firefox-launcher": "^0.1.4", | ||
"karma-mocha": "^0.1.10", | ||
"karma-mocha": "^0.2.0", | ||
"karma-mocha-reporter": "^1.1.0", | ||
"karma-sourcemap-loader": "^0.3.5", | ||
"karma-webpack": "^1.7.0", | ||
"mocha": "^2.2.5", | ||
"node-sass": "^3.2.0", | ||
"react-a11y": "0.1.1", | ||
"react-hot-loader": "1.2.7", | ||
"react-a11y": "0.2.6", | ||
"react-hot-loader": "1.2.8", | ||
"redux-devtools": "1.0.2", | ||
"sass-loader": "^2.0.0", | ||
"strip-loader": "^0.1.0", | ||
"style-loader": "^0.12.3", | ||
"webpack": "^1.9.11", | ||
"webpack-dev-server": "1.9.0" | ||
"webpack-dev-server": "1.10.1" | ||
}, | ||
"engines": { | ||
"node": ">=0.10.32" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.