Skip to content

Commit e3bae90

Browse files
committed
Merge pull request #1269 from rackt/tree-view-example
Add tree view example
2 parents dedc539 + 5bb02d3 commit e3bae90

File tree

13 files changed

+335
-0
lines changed

13 files changed

+335
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ For PDF, ePub, and MOBI exports for offline reading, and instructions on how to
160160
* [Universal](http://rackt.github.io/redux/docs/introduction/Examples.html#universal) ([source](https://github.com/rackt/redux/tree/master/examples/universal))
161161
* [Real World](http://rackt.github.io/redux/docs/introduction/Examples.html#real-world) ([source](https://github.com/rackt/redux/tree/master/examples/real-world))
162162
* [Shopping Cart](http://rackt.github.io/redux/docs/introduction/Examples.html#shopping-cart) ([source](https://github.com/rackt/redux/tree/master/examples/shopping-cart))
163+
* [Tree View](http://rackt.github.io/redux/docs/introduction/Examples.html#tree-view) ([source](https://github.com/rackt/redux/tree/master/examples/tree-view))
163164

164165
If you’re new to the NPM ecosystem and have troubles getting a project up and running, or aren’t sure where to paste the gist above, check out [simplest-redux-example](https://github.com/jackielii/simplest-redux-example) that uses Redux together with React and Browserify.
165166

docs/introduction/Examples.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,29 @@ It covers:
153153
* Using only [React Redux](https://github.com/rackt/react-redux) to bind action creators
154154
* Conditional middleware (logging example)
155155

156+
## Tree View
157+
158+
Run the [Tree View](https://github.com/rackt/redux/tree/master/examples/tree-view) example:
159+
160+
```
161+
git clone https://github.com/rackt/redux.git
162+
163+
cd redux/examples/tree-view
164+
npm install
165+
npm start
166+
167+
open http://localhost:3000/
168+
```
169+
170+
This is an example of performant rendering.
171+
172+
It covers:
173+
174+
* Normalized state
175+
* Reducer composition
176+
* State representing a tree view
177+
* Granual re-rendering of a large subtree
178+
156179
## More Examples
157180

158181
You can find more examples in [Awesome Redux](https://github.com/xgrommx/awesome-redux).

examples/tree-view/.babelrc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"stage": 2,
3+
"env": {
4+
"development": {
5+
"plugins": [
6+
"react-transform"
7+
],
8+
"extra": {
9+
"react-transform": {
10+
"transforms": [{
11+
"transform": "react-transform-hmr",
12+
"imports": ["react"],
13+
"locals": ["module"]
14+
}]
15+
}
16+
}
17+
}
18+
}
19+
}

examples/tree-view/actions/index.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export const INCREMENT = 'INCREMENT'
2+
export const CREATE_NODE = 'CREATE_NODE'
3+
export const ADD_CHILD = 'ADD_CHILD'
4+
5+
export function increment(nodeId) {
6+
return {
7+
type: INCREMENT,
8+
nodeId
9+
}
10+
}
11+
12+
let nextId = 0
13+
export function createNode() {
14+
return {
15+
type: CREATE_NODE,
16+
nodeId: `new_${nextId++}`
17+
}
18+
}
19+
20+
export function addChild(nodeId, childId) {
21+
return {
22+
type: ADD_CHILD,
23+
nodeId,
24+
childId
25+
}
26+
}

examples/tree-view/containers/Node.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react'
2+
import { Component } from 'react'
3+
import { connect } from 'react-redux'
4+
import * as actions from '../actions'
5+
6+
class Node extends Component {
7+
constructor(props) {
8+
super(props)
9+
this.handleIncrementClick = this.handleIncrementClick.bind(this)
10+
this.handleAddChildClick = this.handleAddChildClick.bind(this)
11+
}
12+
13+
handleIncrementClick() {
14+
const { increment, id } = this.props
15+
increment(id)
16+
}
17+
18+
handleAddChildClick(e) {
19+
e.preventDefault()
20+
21+
const { addChild, createNode, id } = this.props
22+
const childId = createNode().nodeId
23+
addChild(id, childId)
24+
}
25+
26+
renderChild(childId) {
27+
return (
28+
<li key={childId}>
29+
<ConnectedNode id={childId} />
30+
</li>
31+
)
32+
}
33+
34+
render() {
35+
const { counter, childIds } = this.props
36+
return (
37+
<div>
38+
Counter: {counter}
39+
{' '}
40+
<button onClick={this.handleIncrementClick}>
41+
+
42+
</button>
43+
<ul>
44+
{childIds.map(this.renderChild)}
45+
<li key='add'>
46+
<a href='#' onClick={this.handleAddChildClick}>
47+
Add child
48+
</a>
49+
</li>
50+
</ul>
51+
</div>
52+
)
53+
}
54+
}
55+
56+
function mapStateToProps(state, ownProps) {
57+
return state[ownProps.id]
58+
}
59+
60+
const ConnectedNode = connect(mapStateToProps, actions)(Node)
61+
export default ConnectedNode

examples/tree-view/generateTree.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export default function generateTree() {
2+
let tree = {
3+
0: {
4+
id: 0,
5+
counter: 0,
6+
childIds: []
7+
}
8+
}
9+
10+
for (let i = 1; i < 1000; i++) {
11+
let parentId = Math.floor(Math.pow(Math.random(), 2) * i)
12+
tree[i] = {
13+
id: i,
14+
counter: 0,
15+
childIds: []
16+
}
17+
tree[parentId].childIds.push(i)
18+
}
19+
20+
return tree
21+
}

examples/tree-view/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 tree-view example</title>
5+
</head>
6+
<body>
7+
<div id="root">
8+
</div>
9+
<script src="/static/bundle.js"></script>
10+
</body>
11+
</html>

examples/tree-view/index.js

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

examples/tree-view/package.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "redux-tree-view-example",
3+
"version": "0.0.0",
4+
"description": "Redux tree-view example",
5+
"scripts": {
6+
"start": "node server.js"
7+
},
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/rackt/redux.git"
11+
},
12+
"license": "MIT",
13+
"bugs": {
14+
"url": "https://github.com/rackt/redux/issues"
15+
},
16+
"homepage": "http://rackt.github.io/redux",
17+
"dependencies": {
18+
"react": "^0.14.6",
19+
"react-dom": "^0.14.6",
20+
"react-redux": "^4.0.6",
21+
"redux": "^3.0.6"
22+
},
23+
"devDependencies": {
24+
"babel-core": "^5.6.18",
25+
"babel-loader": "^5.1.4",
26+
"babel-plugin-react-transform": "^1.1.0",
27+
"expect": "^1.6.0",
28+
"express": "^4.13.3",
29+
"jsdom": "^5.6.1",
30+
"mocha": "^2.2.5",
31+
"node-libs-browser": "^0.5.2",
32+
"react-transform-hmr": "^1.0.0",
33+
"webpack": "^1.9.11",
34+
"webpack-dev-middleware": "^1.2.0",
35+
"webpack-hot-middleware": "^2.2.0"
36+
}
37+
}

examples/tree-view/reducers/index.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { INCREMENT, ADD_CHILD, CREATE_NODE } from '../actions'
2+
3+
function node(state, action) {
4+
switch (action.type) {
5+
case CREATE_NODE:
6+
return {
7+
id: action.nodeId,
8+
counter: 0,
9+
childIds: []
10+
}
11+
case INCREMENT:
12+
return Object.assign({}, state, {
13+
counter: state.counter + 1
14+
})
15+
case ADD_CHILD:
16+
return Object.assign({}, state, {
17+
childIds: [ ...state.childIds, action.childId ]
18+
})
19+
default:
20+
return state
21+
}
22+
}
23+
24+
export default function (state = {}, action) {
25+
const { nodeId } = action
26+
if (typeof nodeId === 'undefined') {
27+
return state
28+
}
29+
30+
return Object.assign({}, state, {
31+
[nodeId]: node(state[nodeId], action)
32+
})
33+
}
34+

examples/tree-view/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+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createStore } from 'redux'
2+
import reducer from '../reducers'
3+
4+
export default function configureStore(initialState) {
5+
const store = createStore(reducer, initialState)
6+
7+
if (module.hot) {
8+
// Enable Webpack hot module replacement for reducers
9+
module.hot.accept('../reducers', () => {
10+
const nextReducer = require('../reducers')
11+
store.replaceReducer(nextReducer)
12+
})
13+
}
14+
15+
return store
16+
}

examples/tree-view/webpack.config.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
var path = require('path')
2+
var webpack = require('webpack')
3+
4+
module.exports = {
5+
devtool: 'cheap-module-eval-source-map',
6+
entry: [
7+
'webpack-hot-middleware/client',
8+
'./index'
9+
],
10+
output: {
11+
path: path.join(__dirname, 'dist'),
12+
filename: 'bundle.js',
13+
publicPath: '/static/'
14+
},
15+
plugins: [
16+
new webpack.optimize.OccurenceOrderPlugin(),
17+
new webpack.HotModuleReplacementPlugin(),
18+
new webpack.NoErrorsPlugin()
19+
],
20+
module: {
21+
loaders: [
22+
{
23+
test: /\.js$/,
24+
loaders: [ 'babel' ],
25+
exclude: /node_modules/,
26+
include: __dirname
27+
}
28+
]
29+
}
30+
}
31+
32+
33+
// When inside Redux repo, prefer src to compiled version.
34+
// You can safely delete these lines in your project.
35+
var reduxSrc = path.join(__dirname, '..', '..', 'src')
36+
var reduxNodeModules = path.join(__dirname, '..', '..', 'node_modules')
37+
var fs = require('fs')
38+
if (fs.existsSync(reduxSrc) && fs.existsSync(reduxNodeModules)) {
39+
// Resolve Redux to source
40+
module.exports.resolve = { alias: { 'redux': reduxSrc } }
41+
// Compile Redux from source
42+
module.exports.module.loaders.push({
43+
test: /\.js$/,
44+
loaders: [ 'babel' ],
45+
include: reduxSrc
46+
})
47+
}

0 commit comments

Comments
 (0)