Skip to content

Commit

Permalink
Merge pull request reduxjs#1269 from rackt/tree-view-example
Browse files Browse the repository at this point in the history
Add tree view example
  • Loading branch information
gaearon committed Jan 25, 2016
2 parents dedc539 + 5bb02d3 commit e3bae90
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ For PDF, ePub, and MOBI exports for offline reading, and instructions on how to
* [Universal](https://rackt.github.io/redux/docs/introduction/Examples.html#universal) ([source](https://github.com/rackt/redux/tree/master/examples/universal))
* [Real World](https://rackt.github.io/redux/docs/introduction/Examples.html#real-world) ([source](https://github.com/rackt/redux/tree/master/examples/real-world))
* [Shopping Cart](https://rackt.github.io/redux/docs/introduction/Examples.html#shopping-cart) ([source](https://github.com/rackt/redux/tree/master/examples/shopping-cart))
* [Tree View](https://rackt.github.io/redux/docs/introduction/Examples.html#tree-view) ([source](https://github.com/rackt/redux/tree/master/examples/tree-view))

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.

Expand Down
23 changes: 23 additions & 0 deletions docs/introduction/Examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,29 @@ It covers:
* Using only [React Redux](https://github.com/rackt/react-redux) to bind action creators
* Conditional middleware (logging example)

## Tree View

Run the [Tree View](https://github.com/rackt/redux/tree/master/examples/tree-view) example:

```
git clone https://github.com/rackt/redux.git
cd redux/examples/tree-view
npm install
npm start
open https://localhost:3000/
```

This is an example of performant rendering.

It covers:

* Normalized state
* Reducer composition
* State representing a tree view
* Granual re-rendering of a large subtree

## More Examples

You can find more examples in [Awesome Redux](https://github.com/xgrommx/awesome-redux).
19 changes: 19 additions & 0 deletions examples/tree-view/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"stage": 2,
"env": {
"development": {
"plugins": [
"react-transform"
],
"extra": {
"react-transform": {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}
}
}
}
}
26 changes: 26 additions & 0 deletions examples/tree-view/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const INCREMENT = 'INCREMENT'
export const CREATE_NODE = 'CREATE_NODE'
export const ADD_CHILD = 'ADD_CHILD'

export function increment(nodeId) {
return {
type: INCREMENT,
nodeId
}
}

let nextId = 0
export function createNode() {
return {
type: CREATE_NODE,
nodeId: `new_${nextId++}`
}
}

export function addChild(nodeId, childId) {
return {
type: ADD_CHILD,
nodeId,
childId
}
}
61 changes: 61 additions & 0 deletions examples/tree-view/containers/Node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react'
import { Component } from 'react'
import { connect } from 'react-redux'
import * as actions from '../actions'

class Node extends Component {
constructor(props) {
super(props)
this.handleIncrementClick = this.handleIncrementClick.bind(this)
this.handleAddChildClick = this.handleAddChildClick.bind(this)
}

handleIncrementClick() {
const { increment, id } = this.props
increment(id)
}

handleAddChildClick(e) {
e.preventDefault()

const { addChild, createNode, id } = this.props
const childId = createNode().nodeId
addChild(id, childId)
}

renderChild(childId) {
return (
<li key={childId}>
<ConnectedNode id={childId} />
</li>
)
}

render() {
const { counter, childIds } = this.props
return (
<div>
Counter: {counter}
{' '}
<button onClick={this.handleIncrementClick}>
+
</button>
<ul>
{childIds.map(this.renderChild)}
<li key='add'>
<a href='#' onClick={this.handleAddChildClick}>
Add child
</a>
</li>
</ul>
</div>
)
}
}

function mapStateToProps(state, ownProps) {
return state[ownProps.id]
}

const ConnectedNode = connect(mapStateToProps, actions)(Node)
export default ConnectedNode
21 changes: 21 additions & 0 deletions examples/tree-view/generateTree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default function generateTree() {
let tree = {
0: {
id: 0,
counter: 0,
childIds: []
}
}

for (let i = 1; i < 1000; i++) {
let parentId = Math.floor(Math.pow(Math.random(), 2) * i)
tree[i] = {
id: i,
counter: 0,
childIds: []
}
tree[parentId].childIds.push(i)
}

return tree
}
11 changes: 11 additions & 0 deletions examples/tree-view/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Redux tree-view example</title>
</head>
<body>
<div id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
16 changes: 16 additions & 0 deletions examples/tree-view/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import Node from './containers/Node'
import configureStore from './store/configureStore'
import generateTree from './generateTree'

const tree = generateTree()
const store = configureStore(tree)

render(
<Provider store={store}>
<Node id={0} />
</Provider>,
document.getElementById('root')
)
37 changes: 37 additions & 0 deletions examples/tree-view/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "redux-tree-view-example",
"version": "0.0.0",
"description": "Redux tree-view example",
"scripts": {
"start": "node server.js"
},
"repository": {
"type": "git",
"url": "https://github.com/rackt/redux.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rackt/redux/issues"
},
"homepage": "https://rackt.github.io/redux",
"dependencies": {
"react": "^0.14.6",
"react-dom": "^0.14.6",
"react-redux": "^4.0.6",
"redux": "^3.0.6"
},
"devDependencies": {
"babel-core": "^5.6.18",
"babel-loader": "^5.1.4",
"babel-plugin-react-transform": "^1.1.0",
"expect": "^1.6.0",
"express": "^4.13.3",
"jsdom": "^5.6.1",
"mocha": "^2.2.5",
"node-libs-browser": "^0.5.2",
"react-transform-hmr": "^1.0.0",
"webpack": "^1.9.11",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.2.0"
}
}
34 changes: 34 additions & 0 deletions examples/tree-view/reducers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { INCREMENT, ADD_CHILD, CREATE_NODE } from '../actions'

function node(state, action) {
switch (action.type) {
case CREATE_NODE:
return {
id: action.nodeId,
counter: 0,
childIds: []
}
case INCREMENT:
return Object.assign({}, state, {
counter: state.counter + 1
})
case ADD_CHILD:
return Object.assign({}, state, {
childIds: [ ...state.childIds, action.childId ]
})
default:
return state
}
}

export default function (state = {}, action) {
const { nodeId } = action
if (typeof nodeId === 'undefined') {
return state
}

return Object.assign({}, state, {
[nodeId]: node(state[nodeId], action)
})
}

23 changes: 23 additions & 0 deletions examples/tree-view/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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 https://localhost:%s/ in your browser.", port, port)
}
})
16 changes: 16 additions & 0 deletions examples/tree-view/store/configureStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createStore } from 'redux'
import reducer from '../reducers'

export default function configureStore(initialState) {
const store = createStore(reducer, initialState)

if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers')
store.replaceReducer(nextReducer)
})
}

return store
}
47 changes: 47 additions & 0 deletions examples/tree-view/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
var path = require('path')
var webpack = require('webpack')

module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-hot-middleware/client',
'./index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [
{
test: /\.js$/,
loaders: [ 'babel' ],
exclude: /node_modules/,
include: __dirname
}
]
}
}


// When inside Redux repo, prefer src to compiled version.
// You can safely delete these lines in your project.
var reduxSrc = path.join(__dirname, '..', '..', 'src')
var reduxNodeModules = path.join(__dirname, '..', '..', 'node_modules')
var fs = require('fs')
if (fs.existsSync(reduxSrc) && fs.existsSync(reduxNodeModules)) {
// Resolve Redux to source
module.exports.resolve = { alias: { 'redux': reduxSrc } }
// Compile Redux from source
module.exports.module.loaders.push({
test: /\.js$/,
loaders: [ 'babel' ],
include: reduxSrc
})
}

0 comments on commit e3bae90

Please sign in to comment.