diff --git a/examples/async/index.js b/examples/async-with-routing/index.js
similarity index 59%
rename from examples/async/index.js
rename to examples/async-with-routing/index.js
index 12bcb25c37..2c8735b9b7 100644
--- a/examples/async/index.js
+++ b/examples/async-with-routing/index.js
@@ -1,15 +1,19 @@
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
+import Router from 'react-router/lib/Router'
+import browserHistory from 'react-router/lib/browserHistory'
import { Provider } from 'react-redux'
-import App from './containers/App'
+import routes from './routes'
import configureStore from './store/configureStore'
const store = configureStore()
render(
-
+
+ {routes}
+
,
document.getElementById('root')
)
diff --git a/examples/async-with-routing/package.json b/examples/async-with-routing/package.json
new file mode 100644
index 0000000000..805503889b
--- /dev/null
+++ b/examples/async-with-routing/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "redux-async-with-routing-example",
+ "version": "0.0.0",
+ "description": "Redux async with routing example",
+ "scripts": {
+ "start": "node server.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/reactjs/redux.git"
+ },
+ "keywords": [
+ "react",
+ "reactjs",
+ "hot",
+ "reload",
+ "hmr",
+ "live",
+ "edit",
+ "webpack",
+ "flux"
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/reactjs/redux/issues"
+ },
+ "homepage": "http://redux.js.org",
+ "dependencies": {
+ "babel-polyfill": "^6.3.14",
+ "isomorphic-fetch": "^2.1.1",
+ "react": "^0.14.7",
+ "react-dom": "^0.14.7",
+ "react-redux": "^4.2.1",
+ "react-router": "^2.0.0",
+ "redux": "^3.2.1",
+ "redux-logger": "^2.4.0",
+ "redux-thunk": "^1.0.3"
+ },
+ "devDependencies": {
+ "babel-core": "^6.3.15",
+ "babel-loader": "^6.2.0",
+ "babel-preset-es2015": "^6.3.13",
+ "babel-preset-react": "^6.3.13",
+ "babel-preset-react-hmre": "^1.0.1",
+ "expect": "^1.6.0",
+ "express": "^4.13.3",
+ "node-libs-browser": "^0.5.2",
+ "webpack": "^1.9.11",
+ "webpack-dev-middleware": "^1.2.0",
+ "webpack-hot-middleware": "^2.2.0"
+ }
+}
diff --git a/examples/async-with-routing/reducers/index.js b/examples/async-with-routing/reducers/index.js
new file mode 100644
index 0000000000..d6836ee0ff
--- /dev/null
+++ b/examples/async-with-routing/reducers/index.js
@@ -0,0 +1,61 @@
+import { combineReducers } from 'redux'
+import {
+ SELECT_REDDIT, INVALIDATE_REDDIT,
+ REQUEST_POSTS, RECEIVE_POSTS
+} from '../actions'
+
+function selectedReddit(state = '', action) {
+ switch (action.type) {
+ case SELECT_REDDIT:
+ return action.reddit || ''
+ default:
+ return state
+ }
+}
+
+function posts(state = {
+ isFetching: false,
+ didInvalidate: false,
+ items: []
+}, action) {
+ switch (action.type) {
+ case INVALIDATE_REDDIT:
+ return Object.assign({}, state, {
+ didInvalidate: true
+ })
+ case REQUEST_POSTS:
+ return Object.assign({}, state, {
+ isFetching: true,
+ didInvalidate: false
+ })
+ case RECEIVE_POSTS:
+ return Object.assign({}, state, {
+ isFetching: false,
+ didInvalidate: false,
+ items: action.posts,
+ lastUpdated: action.receivedAt
+ })
+ default:
+ return state
+ }
+}
+
+function postsByReddit(state = { }, action) {
+ switch (action.type) {
+ case INVALIDATE_REDDIT:
+ case RECEIVE_POSTS:
+ case REQUEST_POSTS:
+ return Object.assign({}, state, {
+ [action.reddit]: posts(state[action.reddit], action)
+ })
+ default:
+ return state
+ }
+}
+
+const rootReducer = combineReducers({
+ postsByReddit,
+ selectedReddit
+})
+
+export default rootReducer
diff --git a/examples/async-with-routing/routes.js b/examples/async-with-routing/routes.js
new file mode 100644
index 0000000000..2d1dd4abe8
--- /dev/null
+++ b/examples/async-with-routing/routes.js
@@ -0,0 +1,13 @@
+import React from 'react'
+import Route from 'react-router/lib/Route'
+import IndexRoute from 'react-router/lib/IndexRoute'
+
+import App from './components/App'
+import Reddit from './containers/Reddit'
+
+export default(
+
+
+
+
+)
diff --git a/examples/counter/server.js b/examples/async-with-routing/server.js
similarity index 95%
rename from examples/counter/server.js
rename to examples/async-with-routing/server.js
index d655597867..9aeb674633 100644
--- a/examples/counter/server.js
+++ b/examples/async-with-routing/server.js
@@ -10,7 +10,7 @@ var compiler = webpack(config)
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }))
app.use(webpackHotMiddleware(compiler))
-app.get("/", function(req, res) {
+app.get("*", function(req, res) {
res.sendFile(__dirname + '/index.html')
})
diff --git a/examples/async-with-routing/store/configureStore.js b/examples/async-with-routing/store/configureStore.js
new file mode 100644
index 0000000000..465d94919a
--- /dev/null
+++ b/examples/async-with-routing/store/configureStore.js
@@ -0,0 +1,22 @@
+import { createStore, applyMiddleware } from 'redux'
+import thunkMiddleware from 'redux-thunk'
+import createLogger from 'redux-logger'
+import rootReducer from '../reducers'
+
+export default function configureStore(initialState) {
+ const store = createStore(
+ rootReducer,
+ initialState,
+ applyMiddleware(thunkMiddleware, createLogger())
+ )
+
+ if (module.hot) {
+ // Enable Webpack hot module replacement for reducers
+ module.hot.accept('../reducers', () => {
+ const nextRootReducer = require('../reducers').default
+ store.replaceReducer(nextRootReducer)
+ })
+ }
+
+ return store
+}
diff --git a/examples/async/webpack.config.js b/examples/async-with-routing/webpack.config.js
similarity index 91%
rename from examples/async/webpack.config.js
rename to examples/async-with-routing/webpack.config.js
index 2a6d9582c4..35062a810d 100644
--- a/examples/async/webpack.config.js
+++ b/examples/async-with-routing/webpack.config.js
@@ -13,7 +13,7 @@ module.exports = {
publicPath: '/static/'
},
plugins: [
- new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
diff --git a/examples/async/.babelrc b/examples/async/.babelrc
index d0962f5695..064a80f366 100644
--- a/examples/async/.babelrc
+++ b/examples/async/.babelrc
@@ -1,8 +1,4 @@
{
- "presets": ["es2015", "react"],
- "env": {
- "development": {
- "presets": ["react-hmre"]
- }
- }
+ "presets": ["es2015-webpack", "react"],
+ "plugins": ["transform-object-rest-spread"]
}
diff --git a/examples/async/index.html b/examples/async/index.html
deleted file mode 100644
index b89496eae0..0000000000
--- a/examples/async/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- Redux async example
-
-
-
-
-
-
-
diff --git a/examples/async/package.json b/examples/async/package.json
index 6eb76c1b18..f2742b824f 100644
--- a/examples/async/package.json
+++ b/examples/async/package.json
@@ -1,51 +1,38 @@
{
"name": "redux-async-example",
- "version": "0.0.0",
"description": "Redux async example",
"scripts": {
- "start": "node server.js"
+ "start": "node server.dev.js",
+ "build": "webpack --config webpack.config.prod.js"
},
"repository": {
"type": "git",
"url": "https://github.com/reactjs/redux.git"
},
- "keywords": [
- "react",
- "reactjs",
- "hot",
- "reload",
- "hmr",
- "live",
- "edit",
- "webpack",
- "flux"
- ],
"license": "MIT",
- "bugs": {
- "url": "https://github.com/reactjs/redux/issues"
- },
"homepage": "http://redux.js.org",
"dependencies": {
"babel-polyfill": "^6.3.14",
"isomorphic-fetch": "^2.1.1",
- "react": "^0.14.7",
- "react-dom": "^0.14.7",
- "react-redux": "^4.2.1",
- "redux": "^3.2.1",
+ "react": "^15.1.0",
+ "react-dom": "^15.1.0",
+ "react-redux": "^4.4.5",
+ "redux": "^3.5.2",
"redux-logger": "^2.4.0",
"redux-thunk": "^1.0.3"
},
"devDependencies": {
"babel-core": "^6.3.15",
"babel-loader": "^6.2.0",
- "babel-preset-es2015": "^6.3.13",
+ "babel-plugin-transform-object-rest-spread": "^6.8.0",
+ "babel-preset-es2015-webpack": "^6.4.1",
"babel-preset-react": "^6.3.13",
- "babel-preset-react-hmre": "^1.1.1",
"expect": "^1.6.0",
"express": "^4.13.3",
+ "html-webpack-plugin": "^2.20.0",
"node-libs-browser": "^0.5.2",
- "webpack": "^1.9.11",
- "webpack-dev-middleware": "^1.2.0",
- "webpack-hot-middleware": "^2.9.1"
+ "webpack": "^2.1.0-beta.13",
+ "webpack-dev-middleware": "^1.6.1",
+ "webpack-hot-middleware": "^2.10.0"
}
}
diff --git a/examples/async/server.dev.js b/examples/async/server.dev.js
new file mode 100644
index 0000000000..0c6d3a166a
--- /dev/null
+++ b/examples/async/server.dev.js
@@ -0,0 +1,23 @@
+const webpack = require('webpack')
+const express = require('express')
+const webpackDevMiddleware = require('webpack-dev-middleware')
+const webpackHotMiddleware = require('webpack-hot-middleware')
+const config = require('./webpack.config.dev')
+
+const app = express()
+const port = 3000
+const compiler = webpack(config)
+
+app.use(webpackDevMiddleware(compiler, {
+ noInfo: true,
+ publicPath: config.output.publicPath
+}))
+app.use(webpackHotMiddleware(compiler))
+
+app.listen(port, (error) => {
+ if (error) {
+ console.error(error)
+ } else {
+ console.info('🌎 Open http://localhost:%s/ in a web browser', port)
+ }
+})
diff --git a/examples/async/server.js b/examples/async/server.js
deleted file mode 100644
index d655597867..0000000000
--- a/examples/async/server.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var webpack = require('webpack')
-var webpackDevMiddleware = require('webpack-dev-middleware')
-var webpackHotMiddleware = require('webpack-hot-middleware')
-var config = require('./webpack.config')
-
-var app = new (require('express'))()
-var port = 3000
-
-var compiler = webpack(config)
-app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }))
-app.use(webpackHotMiddleware(compiler))
-
-app.get("/", function(req, res) {
- res.sendFile(__dirname + '/index.html')
-})
-
-app.listen(port, function(error) {
- if (error) {
- console.error(error)
- } else {
- console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port)
- }
-})
diff --git a/examples/async/src/actions/index.js b/examples/async/src/actions/index.js
new file mode 100644
index 0000000000..185829539f
--- /dev/null
+++ b/examples/async/src/actions/index.js
@@ -0,0 +1,59 @@
+import fetch from 'isomorphic-fetch'
+
+export function selectReddit(reddit) {
+ return {
+ type: 'SELECT_REDDIT',
+ reddit
+ }
+}
+
+export function invalidateReddit(reddit) {
+ return {
+ type: 'INVALIDATE_REDDIT',
+ reddit
+ }
+}
+
+function requestPosts(reddit) {
+ return {
+ type: 'REQUEST_POSTS',
+ reddit
+ }
+}
+
+function receivePosts(reddit, json) {
+ return {
+ type: 'RECEIVE_POSTS',
+ posts: json.data.children.map(child => child.data),
+ receivedAt: Date.now(),
+ reddit
+ }
+}
+
+function fetchPosts(reddit) {
+ return dispatch => {
+ dispatch(requestPosts(reddit))
+ return fetch(`https://www.reddit.com/r/${reddit}.json`)
+ .then(response => response.json())
+ .then(json => dispatch(receivePosts(reddit, json)))
+ }
+}
+
+function shouldFetchPosts(state, reddit) {
+ const posts = state.postsByReddit[reddit]
+ if (!posts) {
+ return true
+ }
+ if (posts.isFetching) {
+ return false
+ }
+ return posts.didInvalidate
+}
+
+export function fetchPostsIfNeeded(reddit) {
+ return (dispatch, getState) => {
+ if (shouldFetchPosts(getState(), reddit)) {
+ return dispatch(fetchPosts(reddit))
+ }
+ }
+}
diff --git a/examples/async/containers/App.js b/examples/async/src/components/App.js
similarity index 70%
rename from examples/async/containers/App.js
rename to examples/async/src/components/App.js
index 6109878fca..7e98a9792c 100644
--- a/examples/async/containers/App.js
+++ b/examples/async/src/components/App.js
@@ -1,8 +1,8 @@
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
-import { selectReddit, fetchPostsIfNeeded, invalidateReddit } from '../actions'
-import Picker from '../components/Picker'
-import Posts from '../components/Posts'
+import * as actions from '../actions'
+import Picker from './Picker'
+import Posts from './Posts'
class App extends Component {
constructor(props) {
@@ -12,27 +12,30 @@ class App extends Component {
}
componentDidMount() {
- const { dispatch, selectedReddit } = this.props
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ this.maybeFetchPosts()
}
- componentWillReceiveProps(nextProps) {
- if (nextProps.selectedReddit !== this.props.selectedReddit) {
- const { dispatch, selectedReddit } = nextProps
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ componentDidUpdate(prevProps) {
+ if (this.props.selectedReddit !== prevProps.selectedReddit) {
+ this.maybeFetchPosts()
}
}
+ maybeFetchPosts(invalidateCache = false) {
+ const { selectedReddit } = this.props
+ if (invalidateCache) {
+ this.props.invalidateReddit(selectedReddit)
+ }
+ this.props.fetchPostsIfNeeded(selectedReddit)
+ }
+
handleChange(nextReddit) {
- this.props.dispatch(selectReddit(nextReddit))
+ this.props.selectReddit(nextReddit)
}
handleRefreshClick(e) {
e.preventDefault()
-
- const { dispatch, selectedReddit } = this.props
- dispatch(invalidateReddit(selectedReddit))
- dispatch(fetchPostsIfNeeded(selectedReddit))
+ this.maybeFetchPosts(true)
}
render() {
@@ -73,7 +76,9 @@ App.propTypes = {
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
- dispatch: PropTypes.func.isRequired
+ selectReddit: PropTypes.func.isRequired,
+ invalidateReddit: PropTypes.func.isRequired,
+ fetchPostsIfNeeded: PropTypes.func.isRequired
}
function mapStateToProps(state) {
@@ -95,4 +100,4 @@ function mapStateToProps(state) {
}
}
-export default connect(mapStateToProps)(App)
+export default connect(mapStateToProps, actions)(App)
diff --git a/examples/async/src/components/Picker.js b/examples/async/src/components/Picker.js
new file mode 100644
index 0000000000..4b3572e148
--- /dev/null
+++ b/examples/async/src/components/Picker.js
@@ -0,0 +1,25 @@
+import React, { PropTypes } from 'react'
+
+export default function Picker({ value, onChange, options }) {
+ return (
+
+ {value}
+
+
+ )
+}
+
+Picker.propTypes = {
+ options: PropTypes.arrayOf(
+ PropTypes.string.isRequired
+ ).isRequired,
+ value: PropTypes.string.isRequired,
+ onChange: PropTypes.func.isRequired
+}
diff --git a/examples/async/src/components/Posts.js b/examples/async/src/components/Posts.js
new file mode 100644
index 0000000000..3896894f37
--- /dev/null
+++ b/examples/async/src/components/Posts.js
@@ -0,0 +1,15 @@
+import React, { PropTypes } from 'react'
+
+export default function Posts({ posts }) {
+ return (
+
+ {posts.map((post, i) =>
+ - {post.title}
+ )}
+
+ )
+}
+
+Posts.propTypes = {
+ posts: PropTypes.array.isRequired
+}
diff --git a/examples/async/src/components/Root.js b/examples/async/src/components/Root.js
new file mode 100644
index 0000000000..c1646422a0
--- /dev/null
+++ b/examples/async/src/components/Root.js
@@ -0,0 +1,15 @@
+import React, { PropTypes } from 'react';
+import { Provider } from 'react-redux'
+import App from './App'
+
+export default function Root({ store }) {
+ return (
+
+
+
+ )
+}
+
+Root.propTypes = {
+ store: PropTypes.object.isRequired
+}
diff --git a/examples/async/src/index.js b/examples/async/src/index.js
new file mode 100644
index 0000000000..b3ac112969
--- /dev/null
+++ b/examples/async/src/index.js
@@ -0,0 +1,17 @@
+import 'babel-polyfill'
+import React from 'react'
+import { render } from 'react-dom'
+import Root from './components/Root'
+import configureStore from './store/configureStore'
+
+const rootEl = document.createElement('div')
+document.body.appendChild(rootEl)
+
+const store = configureStore()
+render(, rootEl)
+
+if (module.hot) {
+ module.hot.accept('./components/Root', () => {
+ render(, rootEl)
+ })
+}
diff --git a/examples/async/src/reducers/index.js b/examples/async/src/reducers/index.js
new file mode 100644
index 0000000000..d31c805184
--- /dev/null
+++ b/examples/async/src/reducers/index.js
@@ -0,0 +1,61 @@
+import { combineReducers } from 'redux'
+
+function selectedReddit(state = 'reactjs', action) {
+ switch (action.type) {
+ case 'SELECT_REDDIT':
+ return action.reddit
+ default:
+ return state
+ }
+}
+
+function posts(state = {
+ isFetching: false,
+ didInvalidate: false,
+ items: []
+}, action) {
+ switch (action.type) {
+ case 'INVALIDATE_REDDIT':
+ return {
+ ...state,
+ didInvalidate: true
+ }
+ case 'REQUEST_POSTS':
+ return {
+ ...state,
+ isFetching: true,
+ didInvalidate: false
+ }
+ case 'RECEIVE_POSTS':
+ return {
+ ...state,
+ isFetching: false,
+ didInvalidate: false,
+ items: action.posts,
+ lastUpdated: action.receivedAt
+ }
+ default:
+ return state
+ }
+}
+
+function postsByReddit(state = { }, action) {
+ switch (action.type) {
+ case 'INVALIDATE_REDDIT':
+ case 'RECEIVE_POSTS':
+ case 'REQUEST_POSTS':
+ return {
+ ...state,
+ [action.reddit]: posts(state[action.reddit], action)
+ }
+ default:
+ return state
+ }
+}
+
+const reducer = combineReducers({
+ postsByReddit,
+ selectedReddit
+})
+
+export default reducer
diff --git a/examples/async/src/store/configureStore.js b/examples/async/src/store/configureStore.js
new file mode 100644
index 0000000000..f28e73ce5b
--- /dev/null
+++ b/examples/async/src/store/configureStore.js
@@ -0,0 +1,20 @@
+import { createStore, applyMiddleware } from 'redux'
+import thunk from 'redux-thunk'
+import createLogger from 'redux-logger'
+import reducer from '../reducers'
+
+export default function configureStore() {
+ const store = createStore(
+ reducer,
+ applyMiddleware(thunk, createLogger())
+ )
+
+ if (module.hot) {
+ // Enable Webpack hot module replacement for reducers
+ module.hot.accept('../reducers', () => {
+ store.replaceReducer(reducer)
+ })
+ }
+
+ return store
+}
diff --git a/examples/async/webpack.config.dev.js b/examples/async/webpack.config.dev.js
new file mode 100644
index 0000000000..475e66497a
--- /dev/null
+++ b/examples/async/webpack.config.dev.js
@@ -0,0 +1,20 @@
+var path = require('path')
+var webpack = require('webpack')
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+
+module.exports = {
+ devtool: 'cheap-module-eval-source-map',
+ entry: ['./src/index', 'webpack-hot-middleware/client'],
+ plugins: [
+ new webpack.HotModuleReplacementPlugin(),
+ new HtmlWebpackPlugin({ title: 'Redux async example' }),
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' })
+ ],
+ module: {
+ loaders: [{
+ test: /\.js$/,
+ loaders: ['babel'],
+ include: path.join(__dirname, 'src')
+ }]
+ }
+}
diff --git a/examples/async/webpack.config.prod.js b/examples/async/webpack.config.prod.js
new file mode 100644
index 0000000000..3141e9c04c
--- /dev/null
+++ b/examples/async/webpack.config.prod.js
@@ -0,0 +1,25 @@
+var path = require('path')
+var webpack = require('webpack')
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+
+module.exports = {
+ entry: './src/index',
+ output: {
+ path: path.join(__dirname, 'dist'),
+ filename: '[name].[hash].js',
+ publicPath: '/'
+ },
+ plugins: [
+ new HtmlWebpackPlugin({ title: 'Redux async example' }),
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.UglifyJsPlugin()
+ ],
+ module: {
+ loaders: [{
+ test: /\.js$/,
+ loaders: ['babel'],
+ include: path.join(__dirname, 'src')
+ }]
+ }
+}
diff --git a/examples/counter/.babelrc b/examples/counter/.babelrc
index d0962f5695..064a80f366 100644
--- a/examples/counter/.babelrc
+++ b/examples/counter/.babelrc
@@ -1,8 +1,4 @@
{
- "presets": ["es2015", "react"],
- "env": {
- "development": {
- "presets": ["react-hmre"]
- }
- }
+ "presets": ["es2015-webpack", "react"],
+ "plugins": ["transform-object-rest-spread"]
}
diff --git a/examples/counter/package.json b/examples/counter/package.json
index bb69e653c9..7ec41f4a30 100644
--- a/examples/counter/package.json
+++ b/examples/counter/package.json
@@ -3,7 +3,8 @@
"version": "0.0.0",
"description": "Redux counter example",
"scripts": {
- "start": "node server.js",
+ "start": "node server.dev.js",
+ "build": "webpack --config webpack.config.prod.js",
"test": "cross-env NODE_ENV=test mocha --recursive --compilers js:babel-register",
"test:watch": "npm test -- --watch"
},
@@ -25,19 +26,20 @@
"devDependencies": {
"babel-core": "^6.3.15",
"babel-loader": "^6.2.0",
- "babel-preset-es2015": "^6.3.13",
+ "babel-plugin-transform-object-rest-spread": "^6.8.0",
+ "babel-preset-es2015-webpack": "^6.4.1",
"babel-preset-react": "^6.3.13",
- "babel-preset-react-hmre": "^1.1.1",
"babel-register": "^6.3.13",
"cross-env": "^1.0.7",
"enzyme": "^2.0.0",
"expect": "^1.6.0",
"express": "^4.13.3",
+ "html-webpack-plugin": "^2.21.0",
"mocha": "^2.2.5",
"node-libs-browser": "^0.5.2",
"react-addons-test-utils": "^0.14.7",
- "webpack": "^1.9.11",
- "webpack-dev-middleware": "^1.2.0",
- "webpack-hot-middleware": "^2.9.1"
+ "webpack": "^2.1.0-beta.13",
+ "webpack-dev-middleware": "^1.6.1",
+ "webpack-hot-middleware": "^2.10.0"
}
}
diff --git a/examples/counter/server.dev.js b/examples/counter/server.dev.js
new file mode 100644
index 0000000000..0c6d3a166a
--- /dev/null
+++ b/examples/counter/server.dev.js
@@ -0,0 +1,23 @@
+const webpack = require('webpack')
+const express = require('express')
+const webpackDevMiddleware = require('webpack-dev-middleware')
+const webpackHotMiddleware = require('webpack-hot-middleware')
+const config = require('./webpack.config.dev')
+
+const app = express()
+const port = 3000
+const compiler = webpack(config)
+
+app.use(webpackDevMiddleware(compiler, {
+ noInfo: true,
+ publicPath: config.output.publicPath
+}))
+app.use(webpackHotMiddleware(compiler))
+
+app.listen(port, (error) => {
+ if (error) {
+ console.error(error)
+ } else {
+ console.info('🌎 Open http://localhost:%s/ in a web browser', port)
+ }
+})
diff --git a/examples/counter/components/Counter.js b/examples/counter/src/components/Counter.js
similarity index 100%
rename from examples/counter/components/Counter.js
rename to examples/counter/src/components/Counter.js
diff --git a/examples/counter/index.js b/examples/counter/src/index.js
similarity index 68%
rename from examples/counter/index.js
rename to examples/counter/src/index.js
index 60cac9cf27..a0b9319f6c 100644
--- a/examples/counter/index.js
+++ b/examples/counter/src/index.js
@@ -4,8 +4,10 @@ import { createStore } from 'redux'
import Counter from './components/Counter'
import counter from './reducers'
+const rootEl = document.createElement('div')
+document.body.appendChild(rootEl)
+
const store = createStore(counter)
-const rootEl = document.getElementById('root')
function render() {
ReactDOM.render(
@@ -14,9 +16,15 @@ function render() {
onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
/>,
- rootEl
+ rootEl
)
}
render()
store.subscribe(render)
+
+if (module.hot) {
+ module.hot.accept('./components/Counter', () => {
+ render(, rootEl)
+ })
+}
diff --git a/examples/counter/reducers/index.js b/examples/counter/src/reducers/index.js
similarity index 100%
rename from examples/counter/reducers/index.js
rename to examples/counter/src/reducers/index.js
diff --git a/examples/counter/test/.eslintrc b/examples/counter/src/test/.eslintrc
similarity index 100%
rename from examples/counter/test/.eslintrc
rename to examples/counter/src/test/.eslintrc
diff --git a/examples/counter/test/components/Counter.spec.js b/examples/counter/src/test/components/Counter.spec.js
similarity index 100%
rename from examples/counter/test/components/Counter.spec.js
rename to examples/counter/src/test/components/Counter.spec.js
diff --git a/examples/counter/test/reducers/counter.spec.js b/examples/counter/src/test/reducers/counter.spec.js
similarity index 100%
rename from examples/counter/test/reducers/counter.spec.js
rename to examples/counter/src/test/reducers/counter.spec.js
diff --git a/examples/counter/webpack.config.dev.js b/examples/counter/webpack.config.dev.js
new file mode 100644
index 0000000000..2b2991a2b2
--- /dev/null
+++ b/examples/counter/webpack.config.dev.js
@@ -0,0 +1,20 @@
+var path = require('path')
+var webpack = require('webpack')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+
+module.exports = {
+ devtool: 'cheap-module-eval-source-map',
+ entry: [ './src/index', 'webpack-hot-middleware/client' ],
+ plugins: [
+ new webpack.HotModuleReplacementPlugin(),
+ new HtmlWebpackPlugin({ title: 'Redux counter example' }),
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' })
+ ],
+ module: {
+ loaders: [ {
+ test: /\.js$/,
+ loaders: [ 'babel' ],
+ include: path.join(__dirname, 'src')
+ } ]
+ }
+}
diff --git a/examples/counter/webpack.config.prod.js b/examples/counter/webpack.config.prod.js
new file mode 100644
index 0000000000..01314b78b7
--- /dev/null
+++ b/examples/counter/webpack.config.prod.js
@@ -0,0 +1,25 @@
+var path = require('path')
+var webpack = require('webpack')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+
+module.exports = {
+ entry: './src/index',
+ output: {
+ path: path.join(__dirname, 'dist'),
+ filename: '[name].[hash].js',
+ publicPath: '/'
+ },
+ plugins: [
+ new HtmlWebpackPlugin({ title: 'Redux counter example' }),
+ new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.UglifyJsPlugin()
+ ],
+ module: {
+ loaders: [ {
+ test: /\.js$/,
+ loaders: [ 'babel' ],
+ include: path.join(__dirname, 'src')
+ } ]
+ }
+}