Skip to content

Commit 3e56f94

Browse files
author
Ryan Ashcraft
committed
Copy redux/examples/async and update to use Babel 6
1 parent 0424c9b commit 3e56f94

File tree

12 files changed

+449
-0
lines changed

12 files changed

+449
-0
lines changed

examples/async/.babelrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"presets": ["react", "es2015"],
3+
"env": {
4+
"development": {
5+
"presets": ["react-hmre"]
6+
}
7+
}
8+
}

examples/async/actions/index.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import fetch from 'isomorphic-fetch'
2+
3+
export const REQUEST_POSTS = 'REQUEST_POSTS'
4+
export const RECEIVE_POSTS = 'RECEIVE_POSTS'
5+
export const SELECT_REDDIT = 'SELECT_REDDIT'
6+
export const INVALIDATE_REDDIT = 'INVALIDATE_REDDIT'
7+
8+
export function selectReddit(reddit) {
9+
return {
10+
type: SELECT_REDDIT,
11+
reddit
12+
}
13+
}
14+
15+
export function invalidateReddit(reddit) {
16+
return {
17+
type: INVALIDATE_REDDIT,
18+
reddit
19+
}
20+
}
21+
22+
function requestPosts(reddit) {
23+
return {
24+
type: REQUEST_POSTS,
25+
reddit
26+
}
27+
}
28+
29+
function receivePosts(reddit, json) {
30+
return {
31+
type: RECEIVE_POSTS,
32+
reddit: reddit,
33+
posts: json.data.children.map(child => child.data),
34+
receivedAt: Date.now()
35+
}
36+
}
37+
38+
function fetchPosts(reddit) {
39+
return dispatch => {
40+
dispatch(requestPosts(reddit))
41+
return fetch(`https://www.reddit.com/r/${reddit}.json`)
42+
.then(response => response.json())
43+
.then(json => dispatch(receivePosts(reddit, json)))
44+
}
45+
}
46+
47+
function shouldFetchPosts(state, reddit) {
48+
const posts = state.postsByReddit[reddit]
49+
if (!posts) {
50+
return true
51+
}
52+
if (posts.isFetching) {
53+
return false
54+
}
55+
return posts.didInvalidate
56+
}
57+
58+
export function fetchPostsIfNeeded(reddit) {
59+
return (dispatch, getState) => {
60+
if (shouldFetchPosts(getState(), reddit)) {
61+
return dispatch(fetchPosts(reddit))
62+
}
63+
}
64+
}

examples/async/components/Picker.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React, { Component, PropTypes } from 'react'
2+
3+
export default class Picker extends Component {
4+
render() {
5+
const { value, onChange, options } = this.props
6+
7+
return (
8+
<span>
9+
<h1>{value}</h1>
10+
<select onChange={e => onChange(e.target.value)}
11+
value={value}>
12+
{options.map(option =>
13+
<option value={option} key={option}>
14+
{option}
15+
</option>)
16+
}
17+
</select>
18+
</span>
19+
)
20+
}
21+
}
22+
23+
Picker.propTypes = {
24+
options: PropTypes.arrayOf(
25+
PropTypes.string.isRequired
26+
).isRequired,
27+
value: PropTypes.string.isRequired,
28+
onChange: PropTypes.func.isRequired
29+
}

examples/async/components/Posts.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React, { PropTypes, Component } from 'react'
2+
3+
export default class Posts extends Component {
4+
render() {
5+
return (
6+
<ul>
7+
{this.props.posts.map((post, i) =>
8+
<li key={i}>{post.title}</li>
9+
)}
10+
</ul>
11+
)
12+
}
13+
}
14+
15+
Posts.propTypes = {
16+
posts: PropTypes.array.isRequired
17+
}

examples/async/containers/App.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React, { Component, PropTypes } from 'react'
2+
import { connect } from 'react-redux'
3+
import { selectReddit, fetchPostsIfNeeded, invalidateReddit } from '../actions'
4+
import Picker from '../components/Picker'
5+
import Posts from '../components/Posts'
6+
7+
class App extends Component {
8+
constructor(props) {
9+
super(props)
10+
this.handleChange = this.handleChange.bind(this)
11+
this.handleRefreshClick = this.handleRefreshClick.bind(this)
12+
}
13+
14+
componentDidMount() {
15+
const { dispatch, selectedReddit } = this.props
16+
dispatch(fetchPostsIfNeeded(selectedReddit))
17+
}
18+
19+
componentWillReceiveProps(nextProps) {
20+
if (nextProps.selectedReddit !== this.props.selectedReddit) {
21+
const { dispatch, selectedReddit } = nextProps
22+
dispatch(fetchPostsIfNeeded(selectedReddit))
23+
}
24+
}
25+
26+
handleChange(nextReddit) {
27+
this.props.dispatch(selectReddit(nextReddit))
28+
}
29+
30+
handleRefreshClick(e) {
31+
e.preventDefault()
32+
33+
const { dispatch, selectedReddit } = this.props
34+
dispatch(invalidateReddit(selectedReddit))
35+
dispatch(fetchPostsIfNeeded(selectedReddit))
36+
}
37+
38+
render() {
39+
const { selectedReddit, posts, isFetching, lastUpdated } = this.props
40+
return (
41+
<div>
42+
<Picker value={selectedReddit}
43+
onChange={this.handleChange}
44+
options={[ 'reactjs', 'frontend' ]} />
45+
<p>
46+
{lastUpdated &&
47+
<span>
48+
Last updated at {new Date(lastUpdated).toLocaleTimeString()}.
49+
{' '}
50+
</span>
51+
}
52+
{!isFetching &&
53+
<a href="#"
54+
onClick={this.handleRefreshClick}>
55+
Refresh
56+
</a>
57+
}
58+
</p>
59+
{isFetching && posts.length === 0 &&
60+
<h2>Loading...</h2>
61+
}
62+
{!isFetching && posts.length === 0 &&
63+
<h2>Empty.</h2>
64+
}
65+
{posts.length > 0 &&
66+
<div style={{ opacity: isFetching ? 0.5 : 1 }}>
67+
<Posts posts={posts} />
68+
</div>
69+
}
70+
</div>
71+
)
72+
}
73+
}
74+
75+
App.propTypes = {
76+
selectedReddit: PropTypes.string.isRequired,
77+
posts: PropTypes.array.isRequired,
78+
isFetching: PropTypes.bool.isRequired,
79+
lastUpdated: PropTypes.number,
80+
dispatch: PropTypes.func.isRequired
81+
}
82+
83+
function mapStateToProps(state) {
84+
const { selectedReddit, postsByReddit } = state
85+
const {
86+
isFetching,
87+
lastUpdated,
88+
items: posts
89+
} = postsByReddit[selectedReddit] || {
90+
isFetching: true,
91+
items: []
92+
}
93+
94+
return {
95+
selectedReddit,
96+
posts,
97+
isFetching,
98+
lastUpdated
99+
}
100+
}
101+
102+
export default connect(mapStateToProps)(App)

examples/async/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Redux async example</title>
5+
</head>
6+
<body>
7+
<div id="root">
8+
</div>
9+
<script src="/static/bundle.js"></script>
10+
</body>
11+
</html>

examples/async/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import 'babel-polyfill'
2+
import React from 'react'
3+
import { render } from 'react-dom'
4+
import { Provider } from 'react-redux'
5+
import App from './containers/App'
6+
import configureStore from './store/configureStore'
7+
8+
const store = configureStore()
9+
10+
render(
11+
<Provider store={store}>
12+
<App />
13+
</Provider>,
14+
document.getElementById('root')
15+
)

examples/async/package.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"name": "redux-async-example",
3+
"version": "0.0.0",
4+
"description": "Redux async example",
5+
"scripts": {
6+
"start": "node server.js"
7+
},
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/rackt/redux.git"
11+
},
12+
"keywords": [
13+
"react",
14+
"reactjs",
15+
"hot",
16+
"reload",
17+
"hmr",
18+
"live",
19+
"edit",
20+
"webpack",
21+
"flux"
22+
],
23+
"license": "MIT",
24+
"bugs": {
25+
"url": "https://github.com/rackt/redux/issues"
26+
},
27+
"homepage": "http://rackt.github.io/redux",
28+
"dependencies": {
29+
"babel-polyfill": "^6.3.14",
30+
"isomorphic-fetch": "^2.1.1",
31+
"react": "^0.14.0",
32+
"react-dom": "^0.14.0",
33+
"react-redux": "^4.0.0",
34+
"redux": "^3.0.0",
35+
"redux-logger": "^2.0.2",
36+
"redux-thunk": "^0.1.0"
37+
},
38+
"devDependencies": {
39+
"babel-core": "^6.2.1",
40+
"babel-loader": "^6.2.0",
41+
"babel-preset-es2015": "^6.3.13",
42+
"babel-preset-react": "^6.3.13",
43+
"babel-preset-react-hmre": "^1.0.0",
44+
"expect": "^1.6.0",
45+
"express": "^4.13.3",
46+
"node-libs-browser": "^0.5.2",
47+
"webpack": "^1.12.9",
48+
"webpack-dev-middleware": "^1.4.0",
49+
"webpack-hot-middleware": "^2.6.0"
50+
}
51+
}

examples/async/reducers/index.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { combineReducers } from 'redux'
2+
import {
3+
SELECT_REDDIT, INVALIDATE_REDDIT,
4+
REQUEST_POSTS, RECEIVE_POSTS
5+
} from '../actions'
6+
7+
function selectedReddit(state = 'reactjs', action) {
8+
switch (action.type) {
9+
case SELECT_REDDIT:
10+
return action.reddit
11+
default:
12+
return state
13+
}
14+
}
15+
16+
function posts(state = {
17+
isFetching: false,
18+
didInvalidate: false,
19+
items: []
20+
}, action) {
21+
switch (action.type) {
22+
case INVALIDATE_REDDIT:
23+
return Object.assign({}, state, {
24+
didInvalidate: true
25+
})
26+
case REQUEST_POSTS:
27+
return Object.assign({}, state, {
28+
isFetching: true,
29+
didInvalidate: false
30+
})
31+
case RECEIVE_POSTS:
32+
return Object.assign({}, state, {
33+
isFetching: false,
34+
didInvalidate: false,
35+
items: action.posts,
36+
lastUpdated: action.receivedAt
37+
})
38+
default:
39+
return state
40+
}
41+
}
42+
43+
function postsByReddit(state = { }, action) {
44+
switch (action.type) {
45+
case INVALIDATE_REDDIT:
46+
case RECEIVE_POSTS:
47+
case REQUEST_POSTS:
48+
return Object.assign({}, state, {
49+
[action.reddit]: posts(state[action.reddit], action)
50+
})
51+
default:
52+
return state
53+
}
54+
}
55+
56+
const rootReducer = combineReducers({
57+
postsByReddit,
58+
selectedReddit
59+
})
60+
61+
export default rootReducer

examples/async/server.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var webpack = require('webpack')
2+
var webpackDevMiddleware = require('webpack-dev-middleware')
3+
var webpackHotMiddleware = require('webpack-hot-middleware')
4+
var config = require('./webpack.config')
5+
6+
var app = new (require('express'))()
7+
var port = 3000
8+
9+
var compiler = webpack(config)
10+
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }))
11+
app.use(webpackHotMiddleware(compiler))
12+
13+
app.get("/", function(req, res) {
14+
res.sendFile(__dirname + '/index.html')
15+
})
16+
17+
app.listen(port, function(error) {
18+
if (error) {
19+
console.error(error)
20+
} else {
21+
console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port)
22+
}
23+
})

0 commit comments

Comments
 (0)