Skip to content

Commit

Permalink
Merge branch 'master' into heroku
Browse files Browse the repository at this point in the history
* master: (31 commits)
  upgraded redux-form to v1.4.0
  upgraded redux-form to 1.3.4
  rolled back last commit which prematurely tried to upgrade to "history" lib
  added history library
  upgraded to v2.0.0 of redux, react-redux and redux-devtools
  updated redux-form to v1.2.1
  upgraded to redux-form 1.0.1
  upgraded to redux-form 1.0.0
  Fix styles not found in production bug
  moved Ducks doc to its own repo for findability. fixes #146
  fixed readme error to fix #151
  update react-hot-loader
  upgraded to redux-form v0.6.1
  fixed bug in previous commit. oops.
  undid PR #93 to fix #142. thanks, @halt-hammerzeit!
  added the error handling to avoid http-party/node-http-proxy#527
  updated paypal button
  updated paypal button
  added paypal button
  updated redux-form to 0.5.0
  ...
  • Loading branch information
theMoon0777 committed Sep 4, 2015
2 parents 73efd18 + 0e678bb commit 24ae141
Show file tree
Hide file tree
Showing 33 changed files with 377 additions and 384 deletions.
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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=E2LK57ZQ9YRMN)

This is a starter boiler plate app I've put together using the following technologies:

Expand Down Expand Up @@ -49,11 +50,15 @@ 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

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

Expand All @@ -80,6 +85,12 @@ 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/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

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.
Expand Down
3 changes: 3 additions & 0 deletions docs/Ducks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This document has found [another, hopefully permanent, home](https://github.com/erikras/ducks-modular-redux).

Quack.
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = function (config) {
singleRun: true,

frameworks: [ 'mocha' ],

files: [
'tests.webpack.js'
],
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,23 @@
"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",
"react": "0.13.3",
"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.2.4",
"react-redux": "^2.0.0",
"react-router": "v1.0.0-beta2",
"redux": "^2.0.0",
"redux-form": "^1.4.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.8.1"
"webpack-isomorphic-tools": "^0.8.5"
},
"devDependencies": {
"autoprefixer-loader": "^2.0.0",
Expand Down Expand Up @@ -110,8 +111,8 @@
"mocha": "^2.2.5",
"node-sass": "^3.2.0",
"react-a11y": "0.2.6",
"react-hot-loader": "1.2.8",
"redux-devtools": "1.0.2",
"react-hot-loader": "1.3.0",
"redux-devtools": "^2.0.0",
"sass-loader": "^2.0.0",
"strip-loader": "^0.1.0",
"style-loader": "^0.12.3",
Expand Down
21 changes: 0 additions & 21 deletions src/actions/actionTypes.js

This file was deleted.

36 changes: 0 additions & 36 deletions src/actions/authActions.js

This file was deleted.

9 changes: 0 additions & 9 deletions src/actions/counterActions.js

This file was deleted.

12 changes: 0 additions & 12 deletions src/actions/infoActions.js

This file was deleted.

35 changes: 0 additions & 35 deletions src/actions/widgetActions.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/CounterButton.js
Original file line number Diff line number Diff line change
@@ -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}),
Expand Down
2 changes: 1 addition & 1 deletion src/components/InfoBar.js
Original file line number Diff line number Diff line change
@@ -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}),
Expand Down
103 changes: 45 additions & 58 deletions src/components/SurveyForm.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
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';

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});
Expand All @@ -20,87 +20,70 @@ function asyncValidator(data) {
});
}

@connect(state => ({
form: state.surveyForm
}))
@reduxForm('surveyForm', surveyValidation).async(asyncValidator, 'email')
@connectReduxForm({
form: 'survey',
fields: ['name', 'email', 'occupation'],
validate: surveyValidation,
asyncValidate,
asyncBlurFields: ['email']
})
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,
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,
handleBlur,
handleChange,
dirty,
fields: {name, email, occupation},
active,
handleSubmit,
valid,
invalid,
resetForm,
pristine,
dirty
valid
} = this.props;
const styles = require('./SurveyForm.scss');
const renderInput = (field, label, showAsyncValidating) =>
<div className={'form-group' + (field.error && field.touched ? ' has-error' : '')}>
<label htmlFor={field.name} className="col-sm-2">{label}</label>
<div className={'col-sm-8 ' + styles.inputGroup}>
{showAsyncValidating && asyncValidating && <i className={'fa fa-cog fa-spin ' + styles.cog}/>}
<input type="text" className="form-control" id={field.name} {...field}/>
{field.error && field.touched && <div className="text-danger">{field.error}</div>}
<div className={styles.flags}>
{field.dirty && <span className={styles.dirty} title="Dirty">D</span>}
{field.active && <span className={styles.active} title="Active">A</span>}
{field.visited && <span className={styles.visited} title="Visited">V</span>}
{field.touched && <span className={styles.touched} title="Touched">T</span>}
</div>
</div>
</div>;

return (
<div>
<form className="form-horizontal" onSubmit={handleSubmit}>
<div className={'form-group' + (nameError && nameTouched ? ' has-error' : '')}>
<label htmlFor="name" className="col-sm-2">Full Name</label>

<div className="col-sm-10">
<input type="text"
className="form-control"
id="name"
value={name}
onChange={handleChange('name')}
onBlur={handleBlur('name')}/>
{nameError && nameTouched && <div className="text-danger">{nameError}</div>}
</div>
</div>
<div className={'form-group' + (emailError && emailTouched ? ' has-error' : '')}>
<label htmlFor="email" className="col-sm-2">Email address</label>

<div className="col-sm-10">
<input type="email"
className="form-control"
id="email"
value={email}
onChange={handleChange('email')}
onBlur={handleBlur('email')}/>
{emailError && emailTouched && <div className="text-danger">{emailError}</div>}
{asyncValidating && <div>Validating...</div>}
</div>
</div>
<div className={'form-group' + (occupationError && occupationTouched ? ' has-error' : '')}>
<label htmlFor="occupation" className="col-sm-2">Occupation</label>

<div className="col-sm-10">
<input type="text"
className="form-control"
id="occupation"
value={occupation}
onChange={handleChange('occupation')}
onBlur={handleBlur('occupation')}/>
{occupationError && occupationTouched && <div className="text-danger">{occupationError}</div>}
</div>
</div>
{renderInput(name, 'Full Name')}
{renderInput(email, 'Email', true)}
{renderInput(occupation, 'Occupation')}
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button className="btn btn-success" onClick={handleSubmit}>
<i className="fa fa-paper-airplane"/> Submit
<i className="fa fa-paper-plane"/> Submit
</button>
<button className="btn btn-warning" onClick={resetForm} style={{marginLeft:15}}>
<i className="fa fa-undo"/> Reset
</button>
</div>
</div>
Expand All @@ -110,6 +93,10 @@ class SurveyForm extends Component {

<table className="table table-striped">
<tbody>
<tr>
<th>Active Field</th>
<td>{active}</td>
</tr>
<tr>
<th>Dirty</th>
<td className={dirty ? 'success' : 'danger'}>{dirty ? 'true' : 'false'}</td>
Expand Down
Loading

0 comments on commit 24ae141

Please sign in to comment.