From 6758b2afcf3f4f6dd4ac4851ea33dc43a08c4200 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 15:07:26 +0800 Subject: [PATCH 01/31] test build --- karma.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karma.conf.js b/karma.conf.js index 4c574b7..07b4135 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -8,7 +8,7 @@ module.exports = function (config) { singleRun: true, frameworks: [ 'mocha' ], - + files: [ 'tests.webpack.js' ], From cf44ec6106695c37e01d3764b0c394095fee90ea Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 15:35:50 +0200 Subject: [PATCH 02/31] quack! quack! --- src/actions/actionTypes.js | 21 ------ src/actions/authActions.js | 36 ---------- src/actions/counterActions.js | 9 --- src/actions/infoActions.js | 12 ---- src/actions/widgetActions.js | 35 ---------- src/components/CounterButton.js | 2 +- src/components/InfoBar.js | 2 +- src/components/WidgetForm.js | 2 +- src/ducks/auth.js | 103 +++++++++++++++++++++++++++++ src/ducks/counter.js | 23 +++++++ src/{reducers => ducks}/index.js | 0 src/{reducers => ducks}/info.js | 21 +++--- src/{reducers => ducks}/widgets.js | 61 +++++++++++------ src/reducers/auth.js | 80 ---------------------- src/reducers/counter.js | 19 ------ src/redux/create.js | 6 +- src/views/App.js | 6 +- src/views/Login.js | 5 +- src/views/LoginSuccess.js | 5 +- src/views/Widgets.js | 5 +- 20 files changed, 195 insertions(+), 258 deletions(-) delete mode 100644 src/actions/actionTypes.js delete mode 100644 src/actions/authActions.js delete mode 100644 src/actions/counterActions.js delete mode 100644 src/actions/infoActions.js delete mode 100644 src/actions/widgetActions.js create mode 100644 src/ducks/auth.js create mode 100644 src/ducks/counter.js rename src/{reducers => ducks}/index.js (100%) rename src/{reducers => ducks}/info.js (63%) rename src/{reducers => ducks}/widgets.js (55%) delete mode 100644 src/reducers/auth.js delete mode 100644 src/reducers/counter.js diff --git a/src/actions/actionTypes.js b/src/actions/actionTypes.js deleted file mode 100644 index 77d7496..0000000 --- a/src/actions/actionTypes.js +++ /dev/null @@ -1,21 +0,0 @@ -export const INFO_LOAD = 'INFO_LOAD'; -export const INFO_LOAD_SUCCESS = 'INFO_LOAD_SUCCESS'; -export const INFO_LOAD_FAIL = 'INFO_LOAD_FAIL'; -export const WIDGET_LOAD = 'WIDGET_LOAD'; -export const WIDGET_LOAD_SUCCESS = 'WIDGET_LOAD_SUCCESS'; -export const WIDGET_LOAD_FAIL = 'WIDGET_LOAD_FAIL'; -export const WIDGET_EDIT_START = 'WIDGET_EDIT_START'; -export const WIDGET_EDIT_STOP = 'WIDGET_EDIT_STOP'; -export const WIDGET_SAVE = 'WIDGET_SAVE'; -export const WIDGET_SAVE_SUCCESS = 'WIDGET_SAVE_SUCCESS'; -export const WIDGET_SAVE_FAIL = 'WIDGET_SAVE_FAIL'; -export const AUTH_LOAD = 'AUTH_LOAD'; -export const AUTH_LOAD_SUCCESS = 'AUTH_LOAD_SUCCESS'; -export const AUTH_LOAD_FAIL = 'AUTH_LOAD_FAIL'; -export const AUTH_LOGIN = 'AUTH_LOGIN'; -export const AUTH_LOGIN_SUCCESS = 'AUTH_LOGIN_SUCCESS'; -export const AUTH_LOGIN_FAIL = 'AUTH_LOGIN_FAIL'; -export const AUTH_LOGOUT = 'AUTH_LOGOUT'; -export const AUTH_LOGOUT_SUCCESS = 'AUTH_LOGOUT_SUCCESS'; -export const AUTH_LOGOUT_FAIL = 'AUTH_LOGOUT_FAIL'; -export const COUNTER_INCREMENT = 'COUNTER_INCREMENT'; diff --git a/src/actions/authActions.js b/src/actions/authActions.js deleted file mode 100644 index 3fb77e3..0000000 --- a/src/actions/authActions.js +++ /dev/null @@ -1,36 +0,0 @@ -import { - AUTH_LOAD, - AUTH_LOAD_SUCCESS, - AUTH_LOAD_FAIL, - AUTH_LOGIN, - AUTH_LOGIN_SUCCESS, - AUTH_LOGIN_FAIL, - AUTH_LOGOUT, - AUTH_LOGOUT_SUCCESS, - AUTH_LOGOUT_FAIL -} from './actionTypes'; - -export function load() { - return { - types: [AUTH_LOAD, AUTH_LOAD_SUCCESS, AUTH_LOAD_FAIL], - promise: (client) => client.get('/loadAuth') - }; -} - -export function login(name) { - return { - types: [AUTH_LOGIN, AUTH_LOGIN_SUCCESS, AUTH_LOGIN_FAIL], - promise: (client) => client.post('/login', { - data: { - name: name - } - }) - }; -} - -export function logout() { - return { - types: [AUTH_LOGOUT, AUTH_LOGOUT_SUCCESS, AUTH_LOGOUT_FAIL], - promise: (client) => client.get('/logout') - }; -} diff --git a/src/actions/counterActions.js b/src/actions/counterActions.js deleted file mode 100644 index e4fb4be..0000000 --- a/src/actions/counterActions.js +++ /dev/null @@ -1,9 +0,0 @@ -import { - COUNTER_INCREMENT -} from './actionTypes'; - -export function increment() { - return { - type: COUNTER_INCREMENT - }; -} diff --git a/src/actions/infoActions.js b/src/actions/infoActions.js deleted file mode 100644 index a13c33a..0000000 --- a/src/actions/infoActions.js +++ /dev/null @@ -1,12 +0,0 @@ -import { - INFO_LOAD, - INFO_LOAD_SUCCESS, - INFO_LOAD_FAIL -} from './actionTypes'; - -export function load() { - return { - types: [INFO_LOAD, INFO_LOAD_SUCCESS, INFO_LOAD_FAIL], - promise: (client) => client.get('/loadInfo') - }; -} diff --git a/src/actions/widgetActions.js b/src/actions/widgetActions.js deleted file mode 100644 index a1a2c73..0000000 --- a/src/actions/widgetActions.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - WIDGET_LOAD, - WIDGET_LOAD_SUCCESS, - WIDGET_LOAD_FAIL, - WIDGET_EDIT_START, - WIDGET_EDIT_STOP, - WIDGET_SAVE, - WIDGET_SAVE_SUCCESS, - WIDGET_SAVE_FAIL -} from './actionTypes'; - -export function load() { - return { - types: [WIDGET_LOAD, WIDGET_LOAD_SUCCESS, WIDGET_LOAD_FAIL], - promise: (client) => client.get('/loadWidgets') - }; -} - -export function save(widget) { - return { - types: [WIDGET_SAVE, WIDGET_SAVE_SUCCESS, WIDGET_SAVE_FAIL], - id: widget.id, - promise: (client) => client.post('/updateWidget', { - data: widget - }) - }; -} - -export function editStart(id) { - return { type: WIDGET_EDIT_START, id }; -} - -export function editStop(id) { - return { type: WIDGET_EDIT_STOP, id }; -} diff --git a/src/components/CounterButton.js b/src/components/CounterButton.js index 03cda9a..59957e3 100755 --- a/src/components/CounterButton.js +++ b/src/components/CounterButton.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {increment} from '../actions/counterActions'; +import {increment} from '../ducks/counter'; @connect( state => ({count: state.counter.count}), diff --git a/src/components/InfoBar.js b/src/components/InfoBar.js index 5a0c614..8fdc0b7 100755 --- a/src/components/InfoBar.js +++ b/src/components/InfoBar.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {load} from '../actions/infoActions'; +import {load} from '../ducks/info'; @connect( state => ({info: state.info.data}), diff --git a/src/components/WidgetForm.js b/src/components/WidgetForm.js index 2b0610a..c483a55 100755 --- a/src/components/WidgetForm.js +++ b/src/components/WidgetForm.js @@ -3,7 +3,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import reduxForm from 'redux-form'; import widgetValidation, {colors} from '../validation/widgetValidation'; -import * as widgetActions from '../actions/widgetActions'; +import * as widgetActions from '../ducks/widgets'; @connect( state => ({ diff --git a/src/ducks/auth.js b/src/ducks/auth.js new file mode 100644 index 0000000..51228cb --- /dev/null +++ b/src/ducks/auth.js @@ -0,0 +1,103 @@ +const LOAD = 'redux-example/auth/LOAD'; +const LOAD_SUCCESS = 'redux-example/auth/LOAD_SUCCESS'; +const LOAD_FAIL = 'redux-example/auth/LOAD_FAIL'; +const LOGIN = 'redux-example/auth/LOGIN'; +const LOGIN_SUCCESS = 'redux-example/auth/LOGIN_SUCCESS'; +const LOGIN_FAIL = 'redux-example/auth/LOGIN_FAIL'; +const LOGOUT = 'redux-example/auth/LOGOUT'; +const LOGOUT_SUCCESS = 'redux-example/auth/LOGOUT_SUCCESS'; +const LOGOUT_FAIL = 'redux-example/auth/LOGOUT_FAIL'; + +const initialState = { + loaded: false +}; + +export default function reducer(state = initialState, action = {}) { + switch (action.type) { + case LOAD: + return { + ...state, + loading: true + }; + case LOAD_SUCCESS: + return { + ...state, + loading: false, + loaded: true, + user: action.result + }; + case LOAD_FAIL: + return { + ...state, + loading: false, + loaded: false, + error: action.error + }; + case LOGIN: + return { + ...state, + loggingIn: true + }; + case LOGIN_SUCCESS: + return { + ...state, + loggingIn: false, + user: action.result + }; + case LOGIN_FAIL: + return { + ...state, + loggingIn: false, + user: null, + loginError: action.error + }; + case LOGOUT: + return { + ...state, + loggingOut: true + }; + case LOGOUT_SUCCESS: + return { + ...state, + loggingOut: false, + user: null + }; + case LOGOUT_FAIL: + return { + ...state, + loggingOut: false, + logoutError: action.error + }; + default: + return state; + } +} + +export function isLoaded(globalState) { + return globalState.auth && globalState.auth.loaded; +} + +export function load() { + return { + types: [LOAD, LOAD_SUCCESS, LOAD_FAIL], + promise: (client) => client.get('/loadAuth') + }; +} + +export function login(name) { + return { + types: [LOGIN, LOGIN_SUCCESS, LOGIN_FAIL], + promise: (client) => client.post('/login', { + data: { + name: name + } + }) + }; +} + +export function logout() { + return { + types: [LOGOUT, LOGOUT_SUCCESS, LOGOUT_FAIL], + promise: (client) => client.get('/logout') + }; +} diff --git a/src/ducks/counter.js b/src/ducks/counter.js new file mode 100644 index 0000000..74bb67e --- /dev/null +++ b/src/ducks/counter.js @@ -0,0 +1,23 @@ +const INCREMENT = 'redux-example/counter/INCREMENT'; + +const initialState = { + count: 0 +}; + +export default function reducer(state = initialState, action = {}) { + switch (action.type) { + case INCREMENT: + const {count} = state; + return { + count: count + 1 + }; + default: + return state; + } +} + +export function increment() { + return { + type: INCREMENT + }; +} diff --git a/src/reducers/index.js b/src/ducks/index.js similarity index 100% rename from src/reducers/index.js rename to src/ducks/index.js diff --git a/src/reducers/info.js b/src/ducks/info.js similarity index 63% rename from src/reducers/info.js rename to src/ducks/info.js index 5633ae8..bca72b1 100644 --- a/src/reducers/info.js +++ b/src/ducks/info.js @@ -1,8 +1,6 @@ -import { - INFO_LOAD, - INFO_LOAD_SUCCESS, - INFO_LOAD_FAIL -} from '../actions/actionTypes'; +const LOAD = 'redux-example/LOAD'; +const LOAD_SUCCESS = 'redux-example/LOAD_SUCCESS'; +const LOAD_FAIL = 'redux-example/LOAD_FAIL'; const initialState = { loaded: false @@ -10,19 +8,19 @@ const initialState = { export default function info(state = initialState, action = {}) { switch (action.type) { - case INFO_LOAD: + case LOAD: return { ...state, loading: true }; - case INFO_LOAD_SUCCESS: + case LOAD_SUCCESS: return { ...state, loading: false, loaded: true, data: action.result }; - case INFO_LOAD_FAIL: + case LOAD_FAIL: return { ...state, loading: false, @@ -37,3 +35,10 @@ export default function info(state = initialState, action = {}) { export function isLoaded(globalState) { return globalState.info && globalState.info.loaded; } + +export function load() { + return { + types: [LOAD, LOAD_SUCCESS, LOAD_FAIL], + promise: (client) => client.get('/loadInfo') + }; +} diff --git a/src/reducers/widgets.js b/src/ducks/widgets.js similarity index 55% rename from src/reducers/widgets.js rename to src/ducks/widgets.js index e8e6652..bc2a0d8 100644 --- a/src/reducers/widgets.js +++ b/src/ducks/widgets.js @@ -1,13 +1,11 @@ -import { - WIDGET_LOAD, - WIDGET_LOAD_SUCCESS, - WIDGET_LOAD_FAIL, - WIDGET_EDIT_START, - WIDGET_EDIT_STOP, - WIDGET_SAVE, - WIDGET_SAVE_SUCCESS, - WIDGET_SAVE_FAIL -} from '../actions/actionTypes'; +const LOAD = 'redux-example/widgets/LOAD'; +const LOAD_SUCCESS = 'redux-example/widgets/LOAD_SUCCESS'; +const LOAD_FAIL = 'redux-example/widgets/LOAD_FAIL'; +const EDIT_START = 'redux-example/widgets/EDIT_START'; +const EDIT_STOP = 'redux-example/widgets/EDIT_STOP'; +const SAVE = 'redux-example/widgets/SAVE'; +const SAVE_SUCCESS = 'redux-example/widgets/SAVE_SUCCESS'; +const SAVE_FAIL = 'redux-example/widgets/SAVE_FAIL'; const initialState = { loaded: false, @@ -15,14 +13,14 @@ const initialState = { saveError: {} }; -export default function widgets(state = initialState, action = {}) { +export default function reducer(state = initialState, action = {}) { switch (action.type) { - case WIDGET_LOAD: + case LOAD: return { ...state, loading: true }; - case WIDGET_LOAD_SUCCESS: + case LOAD_SUCCESS: return { ...state, loading: false, @@ -30,7 +28,7 @@ export default function widgets(state = initialState, action = {}) { data: action.result, error: null }; - case WIDGET_LOAD_FAIL: + case LOAD_FAIL: return { ...state, loading: false, @@ -38,7 +36,7 @@ export default function widgets(state = initialState, action = {}) { data: null, error: action.error }; - case WIDGET_EDIT_START: + case EDIT_START: return { ...state, editing: { @@ -46,7 +44,7 @@ export default function widgets(state = initialState, action = {}) { [action.id]: true } }; - case WIDGET_EDIT_STOP: + case EDIT_STOP: return { ...state, editing: { @@ -54,9 +52,9 @@ export default function widgets(state = initialState, action = {}) { [action.id]: false } }; - case WIDGET_SAVE: + case SAVE: return state; // 'saving' flag handled by redux-form - case WIDGET_SAVE_SUCCESS: + case SAVE_SUCCESS: const data = [...state.data]; data[action.result.id - 1] = action.result; return { @@ -71,7 +69,7 @@ export default function widgets(state = initialState, action = {}) { [action.id]: null } }; - case WIDGET_SAVE_FAIL: + case SAVE_FAIL: return { ...state, saveError: { @@ -87,3 +85,28 @@ export default function widgets(state = initialState, action = {}) { export function isLoaded(globalState) { return globalState.widgets && globalState.widgets.loaded; } + +export function load() { + return { + types: [LOAD, LOAD_SUCCESS, LOAD_FAIL], + promise: (client) => client.get('/loadWidgets') + }; +} + +export function save(widget) { + return { + types: [SAVE, SAVE_SUCCESS, SAVE_FAIL], + id: widget.id, + promise: (client) => client.post('/updateWidget', { + data: widget + }) + }; +} + +export function editStart(id) { + return { type: EDIT_START, id }; +} + +export function editStop(id) { + return { type: EDIT_STOP, id }; +} diff --git a/src/reducers/auth.js b/src/reducers/auth.js deleted file mode 100644 index d3b868b..0000000 --- a/src/reducers/auth.js +++ /dev/null @@ -1,80 +0,0 @@ -import { - AUTH_LOAD, - AUTH_LOAD_SUCCESS, - AUTH_LOAD_FAIL, - AUTH_LOGIN, - AUTH_LOGIN_SUCCESS, - AUTH_LOGIN_FAIL, - AUTH_LOGOUT, - AUTH_LOGOUT_SUCCESS, - AUTH_LOGOUT_FAIL -} from '../actions/actionTypes'; - -const initialState = { - loaded: false -}; - -export default function info(state = initialState, action = {}) { - switch (action.type) { - case AUTH_LOAD: - return { - ...state, - loading: true - }; - case AUTH_LOAD_SUCCESS: - return { - ...state, - loading: false, - loaded: true, - user: action.result - }; - case AUTH_LOAD_FAIL: - return { - ...state, - loading: false, - loaded: false, - error: action.error - }; - case AUTH_LOGIN: - return { - ...state, - loggingIn: true - }; - case AUTH_LOGIN_SUCCESS: - return { - ...state, - loggingIn: false, - user: action.result - }; - case AUTH_LOGIN_FAIL: - return { - ...state, - loggingIn: false, - user: null, - loginError: action.error - }; - case AUTH_LOGOUT: - return { - ...state, - loggingOut: true - }; - case AUTH_LOGOUT_SUCCESS: - return { - ...state, - loggingOut: false, - user: null - }; - case AUTH_LOGOUT_FAIL: - return { - ...state, - loggingOut: false, - logoutError: action.error - }; - default: - return state; - } -} - -export function isLoaded(globalState) { - return globalState.auth && globalState.auth.loaded; -} diff --git a/src/reducers/counter.js b/src/reducers/counter.js deleted file mode 100644 index 46730b4..0000000 --- a/src/reducers/counter.js +++ /dev/null @@ -1,19 +0,0 @@ -import { - COUNTER_INCREMENT -} from '../actions/actionTypes'; - -const initialState = { - count: 0 -}; - -export default function counter(state = initialState, action = {}) { - switch (action.type) { - case COUNTER_INCREMENT: - const {count} = state; - return { - count: count + 1 - }; - default: - return state; - } -} diff --git a/src/redux/create.js b/src/redux/create.js index 84e7266..dc4d965 100644 --- a/src/redux/create.js +++ b/src/redux/create.js @@ -16,13 +16,13 @@ export default function createApiClientStore(client, data) { finalCreateStore = applyMiddleware(middleware)(createStore); } - const reducer = combineReducers(require('../reducers/index')); + const reducer = combineReducers(require('../ducks/index')); const store = finalCreateStore(reducer, data); store.client = client; if (module.hot) { - module.hot.accept('../reducers/index', () => { - const nextReducer = combineReducers(require('../reducers/index')); + module.hot.accept('../ducks/index', () => { + const nextReducer = combineReducers(require('../ducks/index')); store.replaceReducer(nextReducer); }); } diff --git a/src/views/App.js b/src/views/App.js index af8fa7a..fd7be99 100755 --- a/src/views/App.js +++ b/src/views/App.js @@ -3,10 +3,8 @@ import {Link} from 'react-router'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import DocumentMeta from 'react-document-meta'; -import {isLoaded as isInfoLoaded} from '../reducers/info'; -import {isLoaded as isAuthLoaded} from '../reducers/auth'; -import {load as loadInfo} from '../actions/infoActions'; -import {load as loadAuth, logout} from '../actions/authActions'; +import {isLoaded as isInfoLoaded, load as loadInfo} from '../ducks/info'; +import {isLoaded as isAuthLoaded, load as loadAuth, logout} from '../ducks/auth'; import InfoBar from '../components/InfoBar'; import {createTransitionHook} from '../universalRouter'; diff --git a/src/views/Login.js b/src/views/Login.js index a322fdd..a57fffa 100755 --- a/src/views/Login.js +++ b/src/views/Login.js @@ -2,9 +2,8 @@ import React, {Component, PropTypes} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; import DocumentMeta from 'react-document-meta'; -import {isLoaded as isAuthLoaded} from '../reducers/auth'; -import * as authActions from '../actions/authActions'; -import {load as loadAuth} from '../actions/authActions'; +import * as authActions from '../ducks/auth'; +import {isLoaded as isAuthLoaded, load as loadAuth} from '../ducks/auth'; @connect( state => ({user: state.auth.user}), diff --git a/src/views/LoginSuccess.js b/src/views/LoginSuccess.js index e1a9e14..91d336c 100755 --- a/src/views/LoginSuccess.js +++ b/src/views/LoginSuccess.js @@ -1,9 +1,8 @@ import React, {Component, PropTypes} from 'react'; import {bindActionCreators} from 'redux'; import {connect} from 'react-redux'; -import {isLoaded as isAuthLoaded} from '../reducers/auth'; -import {load as loadAuth} from '../actions/authActions'; -import * as authActions from '../actions/authActions'; +import {isLoaded as isAuthLoaded, load as loadAuth} from '../ducks/auth'; +import * as authActions from '../ducks/auth'; @connect( state => ({user: state.auth.user}), diff --git a/src/views/Widgets.js b/src/views/Widgets.js index 728eaec..8870e26 100755 --- a/src/views/Widgets.js +++ b/src/views/Widgets.js @@ -1,10 +1,9 @@ import React, {Component, PropTypes} from 'react'; import {bindActionCreators} from 'redux'; import DocumentMeta from 'react-document-meta'; -import {isLoaded} from '../reducers/widgets'; import {connect} from 'react-redux'; -import * as widgetActions from '../actions/widgetActions'; -import {load as loadWidgets} from '../actions/widgetActions'; +import * as widgetActions from '../ducks/widgets'; +import {isLoaded, load as loadWidgets} from '../ducks/widgets'; import {initializeWithKey} from 'redux-form'; import WidgetForm from '../components/WidgetForm'; From 272ddc379e79dfa750c4912787c42e97905d3e5d Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 15:54:04 +0200 Subject: [PATCH 03/31] Ducks docs --- README.md | 4 +++ docs/Ducks.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ docs/duck.jpg | Bin 0 -> 7312 bytes 3 files changed, 84 insertions(+) create mode 100644 docs/Ducks.md create mode 100644 docs/duck.jpg diff --git a/README.md b/README.md index 71d4309..13d2f77 100755 --- a/README.md +++ b/README.md @@ -80,6 +80,10 @@ The middleware, [`clientMiddleware.js`](https://github.com/erikras/react-redux-u 1. To allow the action creators access to the client API facade. Remember this is the same on both the client and the server, and cannot simply be `import`ed because it holds the cookie needed to maintain session on server-to-server requests. 2. To allow some actions to pass a "promise generator", a function that takes the API client and returns a promise. Such actions require three action types, the `REQUEST` action that initiates the data loading, and a `SUCCESS` and `FAILURE` action that will be fired depending on the result of the promise. There are other ways to accomplish this, some discussed [here](https://github.com/gaearon/redux/issues/99), which you may prefer, but to the author of this example, the middleware way feels cleanest. +#### What the Duck? + +[Ducks](https://github.com/erikras/react-redux-universal-hot-example/blob/master/docs/Ducks.md) are a Redux Style Proposal that I came up with to better isolate concerns within a Redux application. I encourage you to read the [Ducks Docs](https://github.com/erikras/react-redux-universal-hot-example/blob/master/docs/Ducks.md) and provide feedback. + #### API Server This is where the meat of your server-side application goes. It doesn't have to be implemented in Node or Express at all. This is where you connect to your database and provide authentication and session management. In this example, it's just spitting out some json with the current time stamp. diff --git a/docs/Ducks.md b/docs/Ducks.md new file mode 100644 index 0000000..0a62761 --- /dev/null +++ b/docs/Ducks.md @@ -0,0 +1,80 @@ + + +# Ducks: Redux Reducer Bundles + +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. + +
+ +## 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 + +### 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 actions, ready to be passed to `bindActionCreators()`. + +### 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. \ No newline at end of file diff --git a/docs/duck.jpg b/docs/duck.jpg new file mode 100644 index 0000000000000000000000000000000000000000..139d3ef4111ea14738b2d114f0ad568b502f6699 GIT binary patch literal 7312 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 Grv3*aPTB4N literal 0 HcmV?d00001 From 768e29bede32edb988f45377711b93d1d4a13b73 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 15:56:50 +0200 Subject: [PATCH 04/31] align right duck --- docs/Ducks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Ducks.md b/docs/Ducks.md index 0a62761..1709e91 100644 --- a/docs/Ducks.md +++ b/docs/Ducks.md @@ -1,7 +1,7 @@ - - # Ducks: Redux Reducer Bundles + + 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.
@@ -77,4 +77,4 @@ import * as widgetActions from './ducks/widgets'; 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. \ No newline at end of file +Please submit any feedback via an issue or a tweet to [@erikras](https://twitter.com/erikras). It will be much appreciated. From f55ac7f150faec3ad527ff8e6190df160abd1237 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 16:00:21 +0200 Subject: [PATCH 05/31] added more text to better align the duck --- docs/Ducks.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/Ducks.md b/docs/Ducks.md index 1709e91..dfb6a5d 100644 --- a/docs/Ducks.md +++ b/docs/Ducks.md @@ -4,7 +4,7 @@ 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 @@ -50,6 +50,8 @@ A module... 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". @@ -78,3 +80,7 @@ import * as widgetActions from './ducks/widgets'; 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 \ No newline at end of file From 621a1013375c637f92c60b748fc687a3860c3bf0 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 16:17:26 +0200 Subject: [PATCH 06/31] minor terminology repair --- docs/Ducks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Ducks.md b/docs/Ducks.md index dfb6a5d..b091a3d 100644 --- a/docs/Ducks.md +++ b/docs/Ducks.md @@ -73,7 +73,7 @@ You can still do: ```javascript import * as widgetActions from './ducks/widgets'; ``` -...and it will only import the actions, ready to be passed to `bindActionCreators()`. +...and it will only import the action creators, ready to be passed to `bindActionCreators()`. ### Implementation @@ -83,4 +83,4 @@ Please submit any feedback via an issue or a tweet to [@erikras](https://twitter Happy coding! --- Erik Rasmussen \ No newline at end of file +-- Erik Rasmussen From 76d0c5fad02474ada84ad41b7c36ea5f6783421e Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 16:22:56 +0200 Subject: [PATCH 07/31] added clarifying text about usage --- docs/Ducks.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/Ducks.md b/docs/Ducks.md index b091a3d..cda9b42 100644 --- a/docs/Ducks.md +++ b/docs/Ducks.md @@ -75,6 +75,14 @@ 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. From 44054f51ee80f784529c0a031d3696186455a6fd Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Sat, 22 Aug 2015 10:01:00 +0700 Subject: [PATCH 08/31] Return from thunk --- src/redux/clientMiddleware.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/redux/clientMiddleware.js b/src/redux/clientMiddleware.js index 274fc92..0d718d3 100644 --- a/src/redux/clientMiddleware.js +++ b/src/redux/clientMiddleware.js @@ -1,8 +1,11 @@ export default function clientMiddleware(client) { return ({dispatch, getState}) => { return next => action => { - const { promise, types, ...rest } = - typeof action === 'function' ? action(dispatch, getState) : action; + if (typeof action === 'function') { + return action(dispatch, getState); + } + + const { promise, types, ...rest } = action; if (!promise) { return next(action); } From fe727cc01dabcc0d5447da8d2e0b94d1ce144643 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 21 Aug 2015 21:03:57 -0700 Subject: [PATCH 09/31] Fix typo in README.md "this app can be see" -> "this app can be seen" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13d2f77..5bee93f 100755 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ npm run start ## Demo -A demonstration of this app can be see [running on heroku](https://react-redux.herokuapp.com), which is a deployment of the [heroku branch](https://github.com/erikras/react-redux-universal-hot-example/tree/heroku). +A demonstration of this app can be seen [running on heroku](https://react-redux.herokuapp.com), which is a deployment of the [heroku branch](https://github.com/erikras/react-redux-universal-hot-example/tree/heroku). ## Explanation From 7c2724533d5d7d87579e3b8eed0a05413219ed86 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Tue, 25 Aug 2015 02:59:35 +0200 Subject: [PATCH 10/31] upgraded redux-form to v0.3.0 --- package.json | 3 ++- src/components/SurveyForm.js | 11 +++++++++-- src/components/WidgetForm.js | 14 +++++++------- src/ducks/index.js | 7 ------- src/ducks/reducer.js | 15 +++++++++++++++ src/redux/create.js | 9 ++++----- src/views/Widgets.js | 7 +++---- 7 files changed, 40 insertions(+), 26 deletions(-) delete mode 100644 src/ducks/index.js create mode 100644 src/ducks/reducer.js diff --git a/package.json b/package.json index 099c749..425ef5a 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "file-loader": "^0.8.4", "http-proxy": "^1.11.1", "lru-memoize": "0.0.2", + "map-props": "^0.1.1", "piping": "0.2.0", "pretty-error": "^1.1.2", "query-string": "^2.4.0", @@ -75,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^0.2.4", + "redux-form": "^0.3.0", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index b688f2a..7134767 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react'; import {connect} from 'react-redux'; import reduxForm from 'redux-form'; import surveyValidation from '../validation/surveyValidation'; +import mapProps from 'map-props'; function asyncValidator(data) { // TODO: figure out a way to move this to the server. need an instance of ApiClient @@ -21,9 +22,12 @@ function asyncValidator(data) { } @connect(state => ({ - form: state.surveyForm + form: state.form })) -@reduxForm('surveyForm', surveyValidation).async(asyncValidator, 'email') +@reduxForm('survey', ['name','email','occupation'], surveyValidation).async(asyncValidator, 'email') +@mapProps({ + hasEmail: props => !!props.data.email +}) export default class SurveyForm extends Component { static propTypes = { @@ -31,6 +35,7 @@ class SurveyForm extends Component { data: PropTypes.object.isRequired, dirty: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, + hasEmail: PropTypes.bool.isRequired, handleBlur: PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired, @@ -49,6 +54,7 @@ class SurveyForm extends Component { handleBlur, handleChange, handleSubmit, + hasEmail, valid, invalid, pristine, @@ -105,6 +111,7 @@ class SurveyForm extends Component { + {hasEmail &&
We have email data!
}

Props from redux-form

diff --git a/src/components/WidgetForm.js b/src/components/WidgetForm.js index c483a55..0bc4a03 100755 --- a/src/components/WidgetForm.js +++ b/src/components/WidgetForm.js @@ -7,7 +7,7 @@ import * as widgetActions from '../ducks/widgets'; @connect( state => ({ - form: state.widgetForm, + form: state.form, saveError: state.widgets.saveError }), dispatch => ({ @@ -15,7 +15,7 @@ import * as widgetActions from '../ducks/widgets'; dispatch }) ) -@reduxForm('widgetForm', widgetValidation) +@reduxForm('widget', ['color', 'sprocketCount', 'owner'], widgetValidation) export default class WidgetForm extends Component { static propTypes = { data: PropTypes.object.isRequired, @@ -28,15 +28,15 @@ export default class WidgetForm extends Component { pristine: PropTypes.bool.isRequired, save: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired, - saveError: PropTypes.string, - sliceKey: PropTypes.string.isRequired, + saveError: PropTypes.object, + formKey: PropTypes.string.isRequired, touched: PropTypes.object.isRequired }; render() { - const {sliceKey} = this.props; + const {formKey} = this.props; const { data, editStop, errors, handleBlur, handleChange, handleSubmit, invalid, - pristine, save, submitting, saveError: { [sliceKey]: saveError }, touched } = this.props; + pristine, save, submitting, saveError: { [formKey]: saveError }, touched } = this.props; const styles = require('../views/Widgets.scss'); return ( @@ -69,7 +69,7 @@ export default class WidgetForm extends Component { diff --git a/src/ducks/index.js b/src/ducks/index.js deleted file mode 100644 index 43fbcc1..0000000 --- a/src/ducks/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import {createFormReducer} from 'redux-form'; -export info from './info'; -export widgets from './widgets'; -export auth from './auth'; -export counter from './counter'; -export const surveyForm = createFormReducer('surveyForm', ['name', 'email', 'occupation']); -export const widgetForm = createFormReducer('widgetForm', ['color', 'sprocketCount', 'owner']); diff --git a/src/ducks/reducer.js b/src/ducks/reducer.js new file mode 100644 index 0000000..9ef9dc2 --- /dev/null +++ b/src/ducks/reducer.js @@ -0,0 +1,15 @@ +import { combineReducers } from 'redux'; + +import auth from './auth'; +import counter from './counter'; +import {reducer as form} from 'redux-form'; +import info from './info'; +import widgets from './widgets'; + +export default combineReducers({ + auth, + counter, + form, + info, + widgets +}); diff --git a/src/redux/create.js b/src/redux/create.js index dc4d965..358936c 100644 --- a/src/redux/create.js +++ b/src/redux/create.js @@ -16,14 +16,13 @@ export default function createApiClientStore(client, data) { finalCreateStore = applyMiddleware(middleware)(createStore); } - const reducer = combineReducers(require('../ducks/index')); + const reducer = require('../ducks/reducer'); const store = finalCreateStore(reducer, data); store.client = client; - if (module.hot) { - module.hot.accept('../ducks/index', () => { - const nextReducer = combineReducers(require('../ducks/index')); - store.replaceReducer(nextReducer); + if (__DEVELOPMENT__ && module.hot) { + module.hot.accept('../ducks/reducer', () => { + store.replaceReducer(require('../ducks/reducer')); }); } diff --git a/src/views/Widgets.js b/src/views/Widgets.js index 8870e26..153d834 100755 --- a/src/views/Widgets.js +++ b/src/views/Widgets.js @@ -30,8 +30,7 @@ class Widgets extends Component { initializeWithKey: PropTypes.func.isRequired, editing: PropTypes.object.isRequired, load: PropTypes.func.isRequired, - editStart: PropTypes.func.isRequired, - editStop: PropTypes.func.isRequired + editStart: PropTypes.func.isRequired } render() { @@ -78,7 +77,7 @@ class Widgets extends Component { { widgets.map((widget) => editing[widget.id] ? - : + : {widget.id} {widget.color} @@ -100,7 +99,7 @@ class Widgets extends Component { handleEdit(widget) { const {editStart, initializeWithKey} = this.props; // eslint-disable-line no-shadow return () => { - initializeWithKey('widgetForm', widget.id, widget); + initializeWithKey('widget', widget.id, widget); editStart(String(widget.id)); }; } From 17cf95a847452e72d75df8d00a60181d75834715 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Tue, 25 Aug 2015 21:23:08 +0200 Subject: [PATCH 11/31] updated redux-form to 0.4.0 --- package.json | 2 +- src/components/SurveyForm.js | 14 ++------------ src/components/WidgetForm.js | 10 +++------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 425ef5a..e2ddd0e 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^0.3.0", + "redux-form": "^0.4.0", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index 7134767..40ab640 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -1,6 +1,5 @@ import React, {Component, PropTypes} from 'react'; -import {connect} from 'react-redux'; -import reduxForm from 'redux-form'; +import {connectReduxForm} from 'redux-form'; import surveyValidation from '../validation/surveyValidation'; import mapProps from 'map-props'; @@ -21,13 +20,7 @@ function asyncValidator(data) { }); } -@connect(state => ({ - form: state.form -})) -@reduxForm('survey', ['name','email','occupation'], surveyValidation).async(asyncValidator, 'email') -@mapProps({ - hasEmail: props => !!props.data.email -}) +@connectReduxForm('survey', ['name','email','occupation'], surveyValidation).async(asyncValidator, 'email') export default class SurveyForm extends Component { static propTypes = { @@ -35,7 +28,6 @@ class SurveyForm extends Component { data: PropTypes.object.isRequired, dirty: PropTypes.bool.isRequired, errors: PropTypes.object.isRequired, - hasEmail: PropTypes.bool.isRequired, handleBlur: PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired, @@ -54,7 +46,6 @@ class SurveyForm extends Component { handleBlur, handleChange, handleSubmit, - hasEmail, valid, invalid, pristine, @@ -111,7 +102,6 @@ class SurveyForm extends Component { - {hasEmail &&
We have email data!
}

Props from redux-form

diff --git a/src/components/WidgetForm.js b/src/components/WidgetForm.js index 0bc4a03..5b3f0f7 100755 --- a/src/components/WidgetForm.js +++ b/src/components/WidgetForm.js @@ -1,21 +1,17 @@ import React, {Component, PropTypes} from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import reduxForm from 'redux-form'; +import {connectReduxForm} from 'redux-form'; import widgetValidation, {colors} from '../validation/widgetValidation'; import * as widgetActions from '../ducks/widgets'; @connect( state => ({ - form: state.form, saveError: state.widgets.saveError }), - dispatch => ({ - ...bindActionCreators(widgetActions, dispatch), - dispatch - }) + dispatch => bindActionCreators(widgetActions, dispatch) ) -@reduxForm('widget', ['color', 'sprocketCount', 'owner'], widgetValidation) +@connectReduxForm('widget', ['color', 'sprocketCount', 'owner'], widgetValidation) export default class WidgetForm extends Component { static propTypes = { data: PropTypes.object.isRequired, From 90fb0fd99bb64ac1415e48a443377ebcd509cdb5 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 26 Aug 2015 17:59:16 +0200 Subject: [PATCH 12/31] updated redux-form to 0.5.0 --- package.json | 2 +- src/components/SurveyForm.js | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e2ddd0e..1b6b1e9 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^0.4.0", + "redux-form": "^0.5.0", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index 40ab640..c0c4b26 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -32,6 +32,7 @@ class SurveyForm extends Component { handleChange: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired, invalid: PropTypes.bool.isRequired, + isDirty: PropTypes.func.isRequired, pristine: PropTypes.bool.isRequired, touched: PropTypes.object.isRequired, valid: PropTypes.bool.isRequired @@ -46,6 +47,7 @@ class SurveyForm extends Component { handleBlur, handleChange, handleSubmit, + isDirty, valid, invalid, pristine, @@ -55,7 +57,10 @@ class SurveyForm extends Component {
- +
- +
- +
Date: Wed, 26 Aug 2015 19:35:29 +0200 Subject: [PATCH 13/31] added paypal button --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5bee93f..e12b504 100755 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Demo on Heroku](https://img.shields.io/badge/demo-heroku-lightgrey.png)](https://react-redux.herokuapp.com) [![Dependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example) [![devDependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example/dev-status.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example#info=devDependencies) +[![PayPal donate button](http://img.shields.io/paypal/donate.png?color=yellowgreen)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FJB2AUVJHXQCQ) This is a starter boiler plate app I've put together using the following technologies: From cfaf3e4d68a377c7dda5dc108cfb92adb2632ed7 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 26 Aug 2015 19:39:18 +0200 Subject: [PATCH 14/31] updated paypal button --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e12b504..e1c5c79 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Demo on Heroku](https://img.shields.io/badge/demo-heroku-lightgrey.png)](https://react-redux.herokuapp.com) [![Dependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example) [![devDependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example/dev-status.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example#info=devDependencies) -[![PayPal donate button](http://img.shields.io/paypal/donate.png?color=yellowgreen)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FJB2AUVJHXQCQ) +[![PayPal donate button](http://img.shields.io/paypal/donate.png?color=yellowgreen)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3QQPTMLGV6GU2) This is a starter boiler plate app I've put together using the following technologies: From 845ee702aca77bb004f8bf0acc360908c9e71ea0 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 26 Aug 2015 19:43:35 +0200 Subject: [PATCH 15/31] updated paypal button --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1c5c79..d4af489 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Demo on Heroku](https://img.shields.io/badge/demo-heroku-lightgrey.png)](https://react-redux.herokuapp.com) [![Dependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example) [![devDependency Status](https://david-dm.org/erikras/react-redux-universal-hot-example/dev-status.svg)](https://david-dm.org/erikras/react-redux-universal-hot-example#info=devDependencies) -[![PayPal donate button](http://img.shields.io/paypal/donate.png?color=yellowgreen)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3QQPTMLGV6GU2) +[![PayPal donate button](http://img.shields.io/paypal/donate.png?color=yellowgreen)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=E2LK57ZQ9YRMN) This is a starter boiler plate app I've put together using the following technologies: From fc32ff3ac5ca7a1d87861b3f2981f7b854fc0767 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Thu, 27 Aug 2015 10:18:30 +0800 Subject: [PATCH 16/31] added the error handling to avoid https://github.com/nodejitsu/node-http-proxy/issues/527 --- src/server.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/server.js b/src/server.js index 7c63a3f..37575c6 100755 --- a/src/server.js +++ b/src/server.js @@ -29,6 +29,18 @@ app.use('/api', (req, res) => { proxy.web(req, res); }); +// added the error handling to avoid https://github.com/nodejitsu/node-http-proxy/issues/527 +proxy.on('error', (error, req, res) => { + let json; + console.log('proxy error', error); + if (!res.headersSent) { + res.writeHead(500, {'content-type': 'application/json'}); + } + + json = { error: 'proxy_error', reason: error.message }; + res.end(JSON.stringify(json)); +}); + app.use((req, res) => { if (__DEVELOPMENT__) { // Do not cache webpack stats: the script file would change since From ea40b7ea0ab68f79d142e6974aa28d57827ddfcf Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Thu, 27 Aug 2015 11:48:02 +0200 Subject: [PATCH 17/31] undid PR #93 to fix #142. thanks, @halt-hammerzeit! --- webpack/webpack-dev-server.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/webpack/webpack-dev-server.js b/webpack/webpack-dev-server.js index c256bd0..88f49d0 100755 --- a/webpack/webpack-dev-server.js +++ b/webpack/webpack-dev-server.js @@ -14,11 +14,6 @@ var WebpackDevServer = require('webpack-dev-server'), headers: {"Access-Control-Allow-Origin": "*"}, stats: {colors: true} }, - compiler = webpack(config, function(err, stats){ - var json = stats.toJson(); - if (json.errors.length) - console.error(json.errors[0]) - }), webpackDevServer = new WebpackDevServer(compiler, serverOptions); webpackDevServer.listen(port, host, function() { From b68a93cb80198ad9dcb599644d597d322d5a4cad Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Thu, 27 Aug 2015 11:49:21 +0200 Subject: [PATCH 18/31] fixed bug in previous commit. oops. --- webpack/webpack-dev-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack/webpack-dev-server.js b/webpack/webpack-dev-server.js index 88f49d0..4f20a86 100755 --- a/webpack/webpack-dev-server.js +++ b/webpack/webpack-dev-server.js @@ -14,7 +14,7 @@ var WebpackDevServer = require('webpack-dev-server'), headers: {"Access-Control-Allow-Origin": "*"}, stats: {colors: true} }, - webpackDevServer = new WebpackDevServer(compiler, serverOptions); + webpackDevServer = new WebpackDevServer(webpack(config), serverOptions); webpackDevServer.listen(port, host, function() { console.info('==> 🚧 Webpack development server listening on %s:%s', host, port); From b9996dce67b0570ebd3e7c94f9dcf34cb7b0e6b4 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Thu, 27 Aug 2015 21:45:40 +0200 Subject: [PATCH 19/31] upgraded to redux-form v0.6.1 --- package.json | 2 +- src/components/SurveyForm.js | 45 ++++++++++++++++++------------------ src/components/WidgetForm.js | 28 +++++++++++----------- src/views/Survey.js | 4 +++- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 1b6b1e9..780aefd 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^0.5.0", + "redux-form": "^0.6.1", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index c0c4b26..a306622 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -25,90 +25,89 @@ export default class SurveyForm extends Component { static propTypes = { asyncValidating: PropTypes.bool.isRequired, - data: PropTypes.object.isRequired, + fields: PropTypes.object.isRequired, dirty: PropTypes.bool.isRequired, - errors: PropTypes.object.isRequired, handleBlur: PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired, + resetForm: PropTypes.func.isRequired, invalid: PropTypes.bool.isRequired, - isDirty: PropTypes.func.isRequired, pristine: PropTypes.bool.isRequired, - touched: PropTypes.object.isRequired, valid: PropTypes.bool.isRequired } render() { const { - data: {name, email, occupation}, - errors: {name: nameError, email: emailError, occupation: occupationError}, - touched: {name: nameTouched, email: emailTouched, occupation: occupationTouched}, asyncValidating, + dirty, + fields: {name, email, occupation}, handleBlur, handleChange, handleSubmit, - isDirty, - valid, invalid, + resetForm, pristine, - dirty + valid } = this.props; return (
-
+
- {nameError && nameTouched &&
{nameError}
} + {name.error && name.touched &&
{name.error}
}
-
+
- {emailError && emailTouched &&
{emailError}
} + {email.error && email.touched &&
{email.error}
} {asyncValidating &&
Validating...
}
-
+
- {occupationError && occupationTouched &&
{occupationError}
} + {occupation.error && occupation.touched &&
{occupation.error}
}
+
diff --git a/src/components/WidgetForm.js b/src/components/WidgetForm.js index 5b3f0f7..97eb1a3 100755 --- a/src/components/WidgetForm.js +++ b/src/components/WidgetForm.js @@ -11,11 +11,10 @@ import * as widgetActions from '../ducks/widgets'; }), dispatch => bindActionCreators(widgetActions, dispatch) ) -@connectReduxForm('widget', ['color', 'sprocketCount', 'owner'], widgetValidation) +@connectReduxForm('widget', ['id', 'color', 'sprocketCount', 'owner'], widgetValidation) export default class WidgetForm extends Component { static propTypes = { - data: PropTypes.object.isRequired, - errors: PropTypes.object.isRequired, + fields: PropTypes.object.isRequired, editStop: PropTypes.func.isRequired, handleBlur: PropTypes.func.isRequired, handleChange: PropTypes.func.isRequired, @@ -26,42 +25,41 @@ export default class WidgetForm extends Component { submitting: PropTypes.bool.isRequired, saveError: PropTypes.object, formKey: PropTypes.string.isRequired, - touched: PropTypes.object.isRequired + values: PropTypes.object.isRequired }; render() { - const {formKey} = this.props; - const { data, editStop, errors, handleBlur, handleChange, handleSubmit, invalid, - pristine, save, submitting, saveError: { [formKey]: saveError }, touched } = this.props; + const { editStop, fields: {id, color, sprocketCount, owner}, formKey, handleBlur, handleChange, handleSubmit, invalid, + pristine, save, submitting, saveError: { [formKey]: saveError }, values } = this.props; const styles = require('../views/Widgets.scss'); return ( - {data.id} + {id.value} - {errors.color && touched.color &&
{errors.color}
} + {color.error && color.touched &&
{color.error}
} - {errors.sprocketCount && touched.sprocketCount &&
{errors.sprocketCount}
} + {sprocketCount.error && sprocketCount.touched &&
{sprocketCount.error}
} - {errors.owner && touched.owner &&
{errors.owner}
} + {owner.error && owner.touched &&
{owner.error}
} diff --git a/src/views/Survey.js b/src/views/Survey.js index c81bb08..89b834f 100755 --- a/src/views/Survey.js +++ b/src/views/Survey.js @@ -47,7 +47,9 @@ export default class Survey extends Component {

- +
From 2e844a48d0e6992e2d28333c420ca35ba5d734bd Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 28 Aug 2015 17:20:09 -0500 Subject: [PATCH 20/31] update react-hot-loader --- package.json | 2 +- webpack/dev.config.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 780aefd..8ee949a 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "mocha": "^2.2.5", "node-sass": "^3.2.0", "react-a11y": "0.2.6", - "react-hot-loader": "1.2.8", + "react-hot-loader": "1.3.0", "redux-devtools": "1.0.2", "sass-loader": "^2.0.0", "strip-loader": "^0.1.0", diff --git a/webpack/dev.config.js b/webpack/dev.config.js index 750c9cf..9c3e5ec 100755 --- a/webpack/dev.config.js +++ b/webpack/dev.config.js @@ -45,7 +45,6 @@ module.exports = { // hot reload new webpack.HotModuleReplacementPlugin(), new webpack.IgnorePlugin(/\.json$/), - new webpack.NoErrorsPlugin(), new webpack.DefinePlugin({ __CLIENT__: true, __SERVER__: false, @@ -54,4 +53,4 @@ module.exports = { }), webpackIsomorphicToolsPlugin.development() ] -}; \ No newline at end of file +}; From 41356fa6f66ce3fa804a6b87b3f77c1d86d436ca Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Sun, 30 Aug 2015 20:28:42 +0200 Subject: [PATCH 21/31] fixed readme error to fix #151 --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d4af489..ea8173d 100755 --- a/README.md +++ b/README.md @@ -54,7 +54,11 @@ A demonstration of this app can be seen [running on heroku](https://react-redux. ## Explanation -What initally gets run is `babel.server.js`, which does little more than enable ES6 and ES7 awesomeness in the server-side node code. It then initiates `server.js`. In `server.js` we proxy any requests to `/api/*` to the [API server](#api-server), running at `localhost:3030`. All the data fetching calls from the client go to `/api/*`. Aside from serving the favicon and static content from `/static`, the only thing `server.js` does is initiate delegate rendering to `react-router`. At the bottom of `server.js`, we listen to port `3000` and initiate the API server. +What initally gets run is `bin/server.js`, which does little more than enable ES6 and ES7 awesomeness in the +server-side node code. It then initiates `server.js`. In `server.js` we proxy any requests to `/api/*` to the +[API server](#api-server), running at `localhost:3030`. All the data fetching calls from the client go to `/api/*`. +Aside from serving the favicon and static content from `/static`, the only thing `server.js` does is initiate delegate +rendering to `react-router`. At the bottom of `server.js`, we listen to port `3000` and initiate the API server. #### Routing and HTML return From 1bc8ae818922cbbcd6bc5603eb2d14d5ef230e47 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Mon, 31 Aug 2015 15:16:17 +0200 Subject: [PATCH 22/31] moved Ducks doc to its own repo for findability. fixes #146 --- README.md | 4 ++- docs/Ducks.md | 95 ++------------------------------------------------ docs/duck.jpg | Bin 7312 -> 0 bytes 3 files changed, 5 insertions(+), 94 deletions(-) delete mode 100644 docs/duck.jpg diff --git a/README.md b/README.md index ea8173d..95dc0eb 100755 --- a/README.md +++ b/README.md @@ -87,7 +87,9 @@ The middleware, [`clientMiddleware.js`](https://github.com/erikras/react-redux-u #### What the Duck? -[Ducks](https://github.com/erikras/react-redux-universal-hot-example/blob/master/docs/Ducks.md) are a Redux Style Proposal that I came up with to better isolate concerns within a Redux application. I encourage you to read the [Ducks Docs](https://github.com/erikras/react-redux-universal-hot-example/blob/master/docs/Ducks.md) and provide feedback. +[Ducks](https://github.com/erikras/ducks-modular-redux) are a Redux Style Proposal that I came up with to better +isolate concerns within a Redux application. I encourage you to read the +[Ducks Docs](https://github.com/erikras/ducks-modular-redux) and provide feedback. #### API Server diff --git a/docs/Ducks.md b/docs/Ducks.md index cda9b42..69cffa4 100644 --- a/docs/Ducks.md +++ b/docs/Ducks.md @@ -1,94 +1,3 @@ -# Ducks: Redux Reducer Bundles +This document has found [another, hopefully permanent, home](https://github.com/erikras/ducks-modular-redux). - - -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 +Quack. diff --git a/docs/duck.jpg b/docs/duck.jpg deleted file mode 100644 index 139d3ef4111ea14738b2d114f0ad568b502f6699..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7312 zcma)A2UJr_w?2^21JZ&JKq3T0O6VN~sZyeJq$(t#6M9FbC{hGTq$^iC0tTe_E=_6D zn}~p*G(iCo1Rm-w|9b1cZ@pRPtTkt5?{9yz=bJg_%*o`*dw^9Vz{wr}w6z5QO5mSz zvJNn)`=VU}0TKXw+N=lwC+|VRjy^sf@**NQZ=@~8-42UHySs@5*m{VFAw@+1Mdbhw zTeJ(-2X2RTaKb5Ze{N{uhC5-DxXq+cq9_kFtfSMFATO*@&{bn}kPBK4!>xP?t{5O6 z;O60m^|6HqxVhrIU;Y^tejTL`S9ABm!ljTB!e~)3 zF}SoeQcO}xT3Sp9E-or2E+Tq*Nehcf%1cShi_5_OEZnEjyfF6ihU%Js#yWjc;{J0` z{{H?*e+i_!mxG9yoSfWG3~_PcQww45K%9?lfH2OR=Pv|xtT)=r$-~FV9S8r3Xlv*0 z>!ZYd%JlaT+&un<{ja(D4?`5{|2B1V`>;n_g|+o@_cC^Ocl|4i`i|~C?%s~>9&j}yX*fU17VU)l*>Lff2MQ&xjq~=g z#i6m<>Pplnw+|h5S? zH!RNQFJH_*zL)>i_oo=#JWeC4W4)aGuoz7*cQ^R2LCZV+I~SRM#rxA2^Y2__|J7II zl#IyFsr`?s{;TO!KR>H~3-|QlZ|P%kr+W7~752#{K=(_o0H*bH0g;@{0Cxd$Fc?e* zCMP2!ry@VSX(-9bDQO_o)HKx85PHb3LJy%kLw|-2!o-nmprD|I z(6Z9gv%;VZP}tuc|KG|jfds@ra`F-=1^`l05DCdY>mLA-f=S58 zDJV}H|5pzQDF}SpLl2OENI~SJ6y&7jG(R1G8Zv;-i;-P6U=(K}xAhFeXT9NOrYLKB zZ)hi>%HtJI$cD0%w-2kKy(18kXDLq!kdmH)ke$L%{0dFNaEi_N3&NKB6vMMDEQ@&y z|6bJ)+O}=SBY|K!nFb)B(?|>;20$4&$olb&EE5cb(vt!zr&KNWj_3NrhMz`Hii`Cv z&R6aE6-G9e^rdb$)M}=?1#Ns`>AsRKk$+*_EN$>Rx#mK&$sGy5dS9IzMzo{2u%i{G zh02Nasi(!?nf3(NE$&<3bEzD+Te$0CCjeecFLpl%T6HV#u6$f>EyY&GK%2iuAP3v+ z$2=C3&zop2DX@Hc@S6t!|49jAe}&{f>CxJk5j$?qJ$5`S@T$1UK3g}Z)-z(!6g0J` z*+uJ*|3P=jsY>p%wYN~X>N>lj2KjuW=J=e|-UoX>u|=XF<;#bS@pi}Oq!nP23raKw z)z-Z9iZ=LmD;l+$xFWfvL&qALh>Vvg%}%PkQH9S{~mNjP#VCptFC{z6Wba{W_7vmh*_ccvZ=b`R{}RU6@d=SD_X^ zlY2TS@`_riXO+d5b0MU|@7|U)%Q(Cn>W`r5b(9rJOV+j)DiOi2L=PzF zeBX16#WqwusrGl$FrloR+M;1j4kM=5@)L-VMf5#7_`1Hj*nB1Yh68N&4zaUT%N+3Ze6w4 zk*`H3x2gOl3524>RnB}+5&k_3^y!Y7_rB_F_2f8(JoL<@bu2_=Dzd8`l!}D--|*~o zbLZNJKJe&9dtH{0J3?=RETrOHIGnCQ+rGw`kv%>jQiaeSvH9!1;S{vC5TQ$(3?uq7 z<3X}pFXuyj#OC@2WZm+Ul?Qs}mJeDjWp^a3y@)~+AJNqo7ihEf2Ms=D(O8V#hg53Y zmKQ;NVH1y%b*Lpp6`;KENvE@sGzk-2A6h7Gy>6JVX&$#Z^QmRi#dYDsjTm{8&4HW0 zM0Bcau{1cvmrR!58b1R+0Y2fj9$z`Q6Nq-W0G=p~8FnHmps_E0B6tD_$L~JBhM0-j zK^6~RW&d~rxLnKh%i;28i#aq7Xh@f#mcwrilPh@67e^iGb{}7^dbM^cZIL^p>y8UC zgLUu90!IQL6qV7t2fORUtg89?%*k>~#z;JxleiGzywgn6HF4o+rZmYgSJQp(mnr{4G#`02h?YH}jZ zcJ=I1)a$w&r@lvj2(2cNuP>r!Za^3BAlfYEYOFWj?Q>ew)R;EK&AJzTSy6e>o+ZJq zW|?X#RwRa7(SIuUpOk^f)+z4R zQs59KV5pRv4ySaxWu50dGg?_(@cEdEovf(VvvhLUMt+`9);FV?8 zDcBUuc_Olw!Sn>6`>^Ekz4*A4T$_q_^QOql6*J_GojNbM zTT{apQ;Z)or($J!V+&p2Ne$g|FK*D}M7l_i#PSQ&<>lYCleqqxQ#wuZ!5fvDqxVb) z=wn_guDKiP+uFiGT4i0+2UFX+Rq#(ekD^DKmXs=amsK4#B#_8G;y1MBnY=>^mD-`h ztL^Qd@=|gyEo>_H3av!F*e}Z>UOFG(u3TA;U1%`+reG0JJ5gFV{DS^0Fe4jYQy!*l6Z&1+q5xeQ3VJz_RSxOnNC#Mv>US)I}e4`v*Zd7sZL zJAEtF*nhhyVY4f_H}T3xcZOr=_ud3zBLH^BCpNiSGS40G~BrRVXFGp8ETVk-E!6RuCQ%-#VrG}J_gXkHo<_#F*o(T zq^gvxp^wvL`0(iuqxf2_^E30N`K|)*3SAHfIj-mS(xJsB&b8R{;(eH;3Y&astBS_- zVOt^1`xj{A*<(sv1yw+H$&Pm_f_O;&H*qQ}dH3H=x$FrCZC zyKJk}MI`v<0x~cQt8;D+j5;w!(sf7T1h_rOvxoe)xOM`d_a%(nKgwe_uU`MXcac2k z1(cJ3Nn{ zJwj#Y%OSx|Icv%>nKUYKPETi*?M;U#dDa;Vkx0|yrC_c3TJx=XSqC1z3vnIvD=be; z%hb4aitr%YgfShmkMHi84D%0-w?KIayw=3ON~f zS0pt$ck^04UxD30I*hVZcP$GExMLn9cDnd!n7UM7^#yJzOP!TD0W!x^Q;xdKioJe# ziEC24*p^%}L!YylD;o1kD_I?h)V2!!=Uo7!JKQvw$tVphtbQswzC(oh_4Cp0I()qT zWy!eMelEFUFv;K!CL}fl`5BqpP@6P7dvy^}E<2hcJLWlO?lqXX&{}xi8gDi8>TQu6 zj|htsT^+J5C?7x+lMK}SZBH-7$6k|quBOx)vKFMY;`UJ^3 zmDRppZ(wdxdgEbb1$nKLW-p^|UNMFv^11fG8B$BYZe+Hv_alU2>RtKBu0ibp4#EP2$t)sMGcoBA9r5AR+8_I z_rD`=)H#Af<`43&HQIM^qI|w6ryrpw=DofyR$I2O$E0L*`YTyde(du1=F(Zg9cvMe z#7dywb|vhFi9yZzwTvh$?F-`GHn*oZpVSCVN2x9Nis{>xOZ&;hhs}?dHDt5|tpz;g z+bvf+0lIM~z?ta6l(W3sG8-P=YGKc~GfT7h(i6R>#dH7uprgpRROr)d-q(Rz zWLP&{zNY#-`T7&+xGE=gxznJpcK-O%mrR0HSytdwrTXn9qT zWPphOk=!QTT<|+)EyH0FKDp`P6;WJ*49ioF6p(|?t#Jjs4&;|EzfoYO(}W06@Ni1eBV0%c&nv(sF`7NI)6UhBdp3}<`kgyH zozAZ!a?l-<6A-m~st31_&mUQ*^*fVS@Y?eahx`*XI`z5!b|P<>^UeJqQ;!EJHRp`|H=2Y;s%LcUHVo z&ghiR)r%ARLM2_S_W#fdJGXS4-Pux7=S)K@EiQPL;Ry4z&92(A#V~oM=LnU{)YTQ) z)jGcESTI>d)I3bzsF|(d_%J1eyvu?XQDEVdKYKp9E7oR@yq(5@$&jV$F-F>TK1wWg zIX@|amdIP%&tfim{Y5t>Pl86Rt%wlECaemfzuOy@&L}A`nImJNH|zJ|zHGk|*)343 z>rubY(;y#7KS4TFthjZ>uQv^8Nl9i^*w=xro>YMqt}-J&o#B#k54`i(RQqP&Tulk; znN=DUH{W|mFihrq(t_f{guu@yfX;gScyoRB{*&F=0r(J+dUE>8iZAJ+1u=%r#pr2m z+=tyx+}JZDJ~ij*Ez-z00m7Z&Zl~)+jQUx(^;}tK8(MCxAF8Q3%^ zrc6^*(UsajGdg^|Py-J|1D?|bS&X`&fd$-%1WDs1^I$ZDH_ zJ=NJ7cfjwF|)B(Dja3_39njbRK&82`Lwj0McWL@dMY@WC!h5! znPXI34ah7xdPXmsc2$dJQ&;P0&1X|VdNKj{TP1NS5idL1@VpP>9sI!TK32-w@C~fkBo(?nps-CQY`6-)H_QY%$Y$Uvq z*)ieb%V$HzP;@58H5Weu>GW8E;8<{_;ai3=tq8~FBntj}GrGh)$G)!?NkGC}tuM0+;b}lwru07*WnU zP#~dJ#bH@dpYbgFt7vds7u&Oh3(L0=H+{UrjWf;Bt7AU((6G3m#3)A>PW^J@95oB$ z)M?gX)$uYQf8(>8(6y zShmi2%}DWr){Ff8#}Tw(LPlFw@EH)XEB!r7>Bpur2tUvaYTu2@BE0y{*J2%pQCRSGT=w6`0=o;S@Ioom}REcpo)%|<)R3_>m zMQxzti6PZ_gERpcqT{JCm?j)pq=a~8N{ku+2|+67FHYydud*>xVSAn?3*0Ppz9?;? zO;$TgH%1#iigRP`jmz$Ax*=%UNZg<`dyEKT6`wNU^i5P1w69LxusH#sP-lYdx61Mn zadYa3@A}li7JG8>94L?T{c`jk07enF;rW@}h z?I{FJ)lDoor%CyZc@d2qJ#LTP%hs+s{AvR3Xw+w1RHaPeM?~{$lP-bvS$Rnq4=#pc z6zLPFq+XGvC`4BpQfl$iQcKd5DNzc)+}41Ej~Ytyhl)+$yY-x}3Hl|Km6p23Ggwxp z82Qe4^h$Lb8br3$1)TuwVQ4FZ*Heudk2GKn_F;U#kHSEA57M#*{aum7OYG0eC5f5~ zl`>Upgcto8Atlehthu53Q?k4|$a-4Krno+46!)$>TXJ=B#$ZjQ_39|7i5HCEA_^hsKOkdcn-i!M=*1cv7`x^7bcyZ`aN|Fd$QC42EP~qvtd&peMDDD}2f7 zyY33EYc&$jvvXzsu*uGHj=ZJl1n6ZIQGRQ_{RvZpF?owzf9WsZ|2~hH7FTez)Uy6C z=xz7H2{77cI%yL*-BIDI&@D9)AJUS;cV_k8a7Tf~5&uN<%Y%%$dsA`uhzpL)lQm_l zgWjg!2kJ-kBFRICL@h^O;)yQR*0i*&LWQjDMJoTCi-HFo!0qujrSw8QVX)R4J-F!e2*rK-`;-lX0*rMzK4C@1C`S5)vg#e!a}Z!mPw%|FNE zJ6}WZ%IvcrMnW|LT0ffR9!qZBDc$Ek0i4T}$BiEa`!_`w^pKnYMqAhRl9itq`puL# zOgnp8YPKzWxie;Jb8hZ~&Bm?Rz61BSrCiJ}G!;P^-|i8b+21AxuT7>L7mv?5NoUSW zM@^3(qI4!pW3qQ1vA@NxDd$utE!6ou%J@<@uxA=;UvFZKkC{OoWUxHlnLyQmBKaz5 z+w3j!m7Cq&6imCQ4aLG=Tl+k47qlN2qzUK*seCavVdP)$t|H=8W44T6ot`<7OCTst Grv3*aPTB4N From 364ff102d5b056b6367173259d1e7a859cb336f6 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Mon, 31 Aug 2015 23:47:31 +0300 Subject: [PATCH 23/31] Fix styles not found in production bug --- package.json | 2 +- webpack/webpack-isomorphic-tools.js | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 8ee949a..44ef574 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "serve-static": "^1.10.0", "superagent": "^1.2.0", "url-loader": "^0.5.6", - "webpack-isomorphic-tools": "^0.8.1" + "webpack-isomorphic-tools": "^0.8.5" }, "devDependencies": { "autoprefixer-loader": "^2.0.0", diff --git a/webpack/webpack-isomorphic-tools.js b/webpack/webpack-isomorphic-tools.js index a646001..c90dc77 100644 --- a/webpack/webpack-isomorphic-tools.js +++ b/webpack/webpack-isomorphic-tools.js @@ -18,16 +18,15 @@ module.exports = { }, style_modules: { extension: 'scss', - development: true, - filter: function(m, regex, options) { - if (options.environment === 'production') { + filter: function(m, regex, options, log) { + if (!options.development) { return regex.test(m.name); } //filter by modules with '.scss' inside name string, that also have name and moduleName that end with 'ss'(allows for css, less, sass, and scss extensions) //this ensures that the proper scss module is returned, so that namePrefix variable is no longer needed return (regex.test(m.name) && m.name.slice(-2) === 'ss' && m.reasons[0].moduleName.slice(-2) === 'ss'); }, - naming: function(m) { + naming: function(m, options, log) { //find index of '/src' inside the module name, slice it and resolve path var srcIndex = m.name.indexOf('/src'); var name = '.' + m.name.slice(srcIndex); @@ -40,9 +39,9 @@ module.exports = { } return name; }, - parser: function(m, options) { + parser: function(m, options, log) { if (m.source) { - var regex = options.environment === 'production' ? /module\.exports = ((.|\n)+);/ : /exports\.locals = ((.|\n)+);/; + var regex = options.development ? /exports\.locals = ((.|\n)+);/ : /module\.exports = ((.|\n)+);/; var match = m.source.match(regex); return match ? JSON.parse(match[1]) : {}; } From b0bec08493dc54165800c2382ef296740c7bf460 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Mon, 31 Aug 2015 22:54:49 +0200 Subject: [PATCH 24/31] upgraded to redux-form 1.0.0 --- package.json | 2 +- src/components/SurveyForm.js | 10 ++++++++-- src/components/WidgetForm.js | 6 +++++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8ee949a..3fe4ac0 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^0.6.1", + "redux-form": "^1.0.0", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index a306622..7a7941c 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -3,7 +3,7 @@ import {connectReduxForm} from 'redux-form'; import surveyValidation from '../validation/surveyValidation'; import mapProps from 'map-props'; -function asyncValidator(data) { +function asyncValidate(data) { // TODO: figure out a way to move this to the server. need an instance of ApiClient if (!data.email) { return Promise.resolve({valid: true}); @@ -20,7 +20,13 @@ function asyncValidator(data) { }); } -@connectReduxForm('survey', ['name','email','occupation'], surveyValidation).async(asyncValidator, 'email') +@connectReduxForm({ + form: 'survey', + fields: ['name', 'email', 'occupation'], + validate: surveyValidation, + asyncValidate, + asyncBlurFields: ['email'] +}) export default class SurveyForm extends Component { static propTypes = { diff --git a/src/components/WidgetForm.js b/src/components/WidgetForm.js index 97eb1a3..9501880 100755 --- a/src/components/WidgetForm.js +++ b/src/components/WidgetForm.js @@ -11,7 +11,11 @@ import * as widgetActions from '../ducks/widgets'; }), dispatch => bindActionCreators(widgetActions, dispatch) ) -@connectReduxForm('widget', ['id', 'color', 'sprocketCount', 'owner'], widgetValidation) +@connectReduxForm({ + form: 'widget', + fields: ['id', 'color', 'sprocketCount', 'owner'], + validate: widgetValidation +}) export default class WidgetForm extends Component { static propTypes = { fields: PropTypes.object.isRequired, From afd4afa5191194782c2aaa9cf376cba4fa4fb420 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Mon, 31 Aug 2015 23:08:11 +0200 Subject: [PATCH 25/31] upgraded to redux-form 1.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5f67ecf..5845e82 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^1.0.0", + "redux-form": "^1.0.1", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", From d0df111257ee5c33f2fc6530707dd428c779415e Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Tue, 1 Sep 2015 22:46:17 +0200 Subject: [PATCH 26/31] updated redux-form to v1.2.1 --- package.json | 2 +- src/views/Widgets.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5845e82..45de5f8 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "0.9.0", "react-router": "v1.0.0-beta3", "redux": "^1.0.1", - "redux-form": "^1.0.1", + "redux-form": "^1.2.1", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/views/Widgets.js b/src/views/Widgets.js index 153d834..11d938d 100755 --- a/src/views/Widgets.js +++ b/src/views/Widgets.js @@ -77,7 +77,7 @@ class Widgets extends Component { { widgets.map((widget) => editing[widget.id] ? - : + : {widget.id} {widget.color} @@ -97,9 +97,8 @@ class Widgets extends Component { } handleEdit(widget) { - const {editStart, initializeWithKey} = this.props; // eslint-disable-line no-shadow + const {editStart} = this.props; // eslint-disable-line no-shadow return () => { - initializeWithKey('widget', widget.id, widget); editStart(String(widget.id)); }; } From 57dea8617b942c8e7aa53cc4f6927db1e1cc85c1 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 2 Sep 2015 20:00:47 +0200 Subject: [PATCH 27/31] upgraded to v2.0.0 of redux, react-redux and redux-devtools --- package.json | 6 +++--- src/redux/create.js | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 45de5f8..9d1f39c 100644 --- a/package.json +++ b/package.json @@ -73,9 +73,9 @@ "react": "0.13.3", "react-document-meta": "^0.1.4", "react-inline-css": "1.2.1", - "react-redux": "0.9.0", + "react-redux": "^2.0.0", "react-router": "v1.0.0-beta3", - "redux": "^1.0.1", + "redux": "^2.0.0", "redux-form": "^1.2.1", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", @@ -112,7 +112,7 @@ "node-sass": "^3.2.0", "react-a11y": "0.2.6", "react-hot-loader": "1.3.0", - "redux-devtools": "1.0.2", + "redux-devtools": "^2.0.0", "sass-loader": "^2.0.0", "strip-loader": "^0.1.0", "style-loader": "^0.12.3", diff --git a/src/redux/create.js b/src/redux/create.js index 358936c..a617644 100644 --- a/src/redux/create.js +++ b/src/redux/create.js @@ -9,9 +9,8 @@ export default function createApiClientStore(client, data) { finalCreateStore = compose( applyMiddleware(middleware), devTools(), - persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)), - createStore - ); + persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)) + )(createStore); } else { finalCreateStore = applyMiddleware(middleware)(createStore); } From efe4829765b1a4d44c7ea27db78fa403d8e0dd6b Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 2 Sep 2015 21:19:07 +0200 Subject: [PATCH 28/31] added history library --- package.json | 1 + src/client.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9d1f39c..86fe4ee 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "express": "^4.13.0", "express-session": "^1.11.3", "file-loader": "^0.8.4", + "history": "^1.8.4", "http-proxy": "^1.11.1", "lru-memoize": "0.0.2", "map-props": "^0.1.1", diff --git a/src/client.js b/src/client.js index c7e06eb..d1aa451 100755 --- a/src/client.js +++ b/src/client.js @@ -3,13 +3,13 @@ */ import 'babel/polyfill'; import React from 'react'; -import BrowserHistory from 'react-router/lib/BrowserHistory'; +import createHistory from 'history/lib/createBrowserHistory'; import Location from 'react-router/lib/Location'; import queryString from 'query-string'; import createStore from './redux/create'; import ApiClient from './ApiClient'; import universalRouter from './universalRouter'; -const history = new BrowserHistory(); +const history = createHistory(); const client = new ApiClient(); const dest = document.getElementById('content'); From c02e48b123536bb553a98a072fdf5da98a081da6 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 2 Sep 2015 22:44:27 +0200 Subject: [PATCH 29/31] rolled back last commit which prematurely tried to upgrade to "history" lib --- package.json | 3 +-- src/client.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 86fe4ee..2b63caf 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "express": "^4.13.0", "express-session": "^1.11.3", "file-loader": "^0.8.4", - "history": "^1.8.4", "http-proxy": "^1.11.1", "lru-memoize": "0.0.2", "map-props": "^0.1.1", @@ -75,7 +74,7 @@ "react-document-meta": "^0.1.4", "react-inline-css": "1.2.1", "react-redux": "^2.0.0", - "react-router": "v1.0.0-beta3", + "react-router": "v1.0.0-beta2", "redux": "^2.0.0", "redux-form": "^1.2.1", "serialize-javascript": "^1.0.0", diff --git a/src/client.js b/src/client.js index d1aa451..c7e06eb 100755 --- a/src/client.js +++ b/src/client.js @@ -3,13 +3,13 @@ */ import 'babel/polyfill'; import React from 'react'; -import createHistory from 'history/lib/createBrowserHistory'; +import BrowserHistory from 'react-router/lib/BrowserHistory'; import Location from 'react-router/lib/Location'; import queryString from 'query-string'; import createStore from './redux/create'; import ApiClient from './ApiClient'; import universalRouter from './universalRouter'; -const history = createHistory(); +const history = new BrowserHistory(); const client = new ApiClient(); const dest = document.getElementById('content'); From e2381d838fb4559a18c211e742a51ce5e2f1cf4e Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Wed, 2 Sep 2015 23:59:18 +0200 Subject: [PATCH 30/31] upgraded redux-form to 1.3.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b63caf..a0044c7 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "^2.0.0", "react-router": "v1.0.0-beta2", "redux": "^2.0.0", - "redux-form": "^1.2.1", + "redux-form": "^1.3.4", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", From 0e678bb45b74ebc70b320cf5d34cf04b9f4fa667 Mon Sep 17 00:00:00 2001 From: theMoon0777 Date: Fri, 4 Sep 2015 19:28:43 +0200 Subject: [PATCH 31/31] upgraded redux-form to v1.4.0 --- package.json | 2 +- src/components/SurveyForm.js | 76 +++++++++++----------------------- src/components/SurveyForm.scss | 38 +++++++++++++++++ src/views/Survey.js | 4 ++ 4 files changed, 68 insertions(+), 52 deletions(-) create mode 100755 src/components/SurveyForm.scss diff --git a/package.json b/package.json index a0044c7..2087156 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "react-redux": "^2.0.0", "react-router": "v1.0.0-beta2", "redux": "^2.0.0", - "redux-form": "^1.3.4", + "redux-form": "^1.4.0", "serialize-javascript": "^1.0.0", "serve-favicon": "^2.3.0", "serve-static": "^1.10.0", diff --git a/src/components/SurveyForm.js b/src/components/SurveyForm.js index 7a7941c..f2681b0 100755 --- a/src/components/SurveyForm.js +++ b/src/components/SurveyForm.js @@ -47,66 +47,36 @@ class SurveyForm extends Component { asyncValidating, dirty, fields: {name, email, occupation}, - handleBlur, - handleChange, + active, handleSubmit, invalid, resetForm, pristine, valid } = this.props; + const styles = require('./SurveyForm.scss'); + const renderInput = (field, label, showAsyncValidating) => +
+ +
+ {showAsyncValidating && asyncValidating && } + + {field.error && field.touched &&
{field.error}
} +
+ {field.dirty && D} + {field.active && A} + {field.visited && V} + {field.touched && T} +
+
+
; + return (
-
- - -
- - {name.error && name.touched &&
{name.error}
} -
-
-
- - -
- - {email.error && email.touched &&
{email.error}
} - {asyncValidating &&
Validating...
} -
-
-
- - -
- - {occupation.error && occupation.touched &&
{occupation.error}
} -
-
+ {renderInput(name, 'Full Name')} + {renderInput(email, 'Email', true)} + {renderInput(occupation, 'Occupation')}