Skip to content

Commit 593c891

Browse files
committed
init
0 parents  commit 593c891

9 files changed

+234
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

index.html

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
5+
<title>React.io</title>
6+
<script src="/socket.io/socket.io.js"></script>
7+
<script type="text/javascript" charset="utf-8" src='/build.js'></script>
8+
</head>
9+
10+
<body>
11+
<h1>React.io</h1>
12+
<p>Experiment using React.js and Socket.io to keep components in sync.</p>
13+
<div id='content'></div>
14+
</body>
15+
16+
</html>

index.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/** @jsx React.DOM */
2+
3+
var _ = require('lodash');
4+
var React = require('./react');
5+
var socketMixin = require('./socket-mixin')('http://carl.local:3000');
6+
7+
var person = React.createClass({
8+
9+
updateValue: function (e) {
10+
this.state.person.name = e.target.value;
11+
this.setState({ person: this.state.person });
12+
this.props.onChange(this.state.person);
13+
},
14+
15+
componentWillReceiveProps: function (props) {
16+
this.setState({
17+
person: props.person
18+
});
19+
},
20+
21+
getInitialState: function () {
22+
return {
23+
person: this.props.person
24+
};
25+
},
26+
27+
render: function () {
28+
return (
29+
<div>
30+
<h2>{this.state.name}</h2>
31+
<input type='text' onChange={this.updateValue} value={this.state.person.name}/>
32+
</div>
33+
);
34+
}
35+
36+
});
37+
38+
var people = React.createClass({
39+
40+
mixins: [socketMixin],
41+
42+
updateValue: function (e) {
43+
this.setState({ name: e.target.value });
44+
},
45+
46+
add: function () {
47+
this.state.people.push({ name: this.state.name });
48+
this.setState({ people: this.state.people, name: '' });
49+
},
50+
51+
remove: function (person) {
52+
this.state.people.splice(this.state.people.indexOf(person), 1)
53+
this.setState({ people: this.state.people });
54+
},
55+
56+
change: function (person) {
57+
this.setState(this.state);
58+
},
59+
60+
getInitialState: function () {
61+
return {
62+
people: [{ id: 0, name: 'Jimi' }, { id: 1, name: 'Stevie' }, { id: 2, name: 'eric' } ],
63+
name: ''
64+
};
65+
},
66+
67+
render: function () {
68+
69+
var list = this.state.people.map(function (data) {
70+
return (
71+
<li>
72+
<person person={data} onChange={this.change}></person>
73+
<button onClick={this.remove.bind(this, data)}>Remove</button>
74+
</li>
75+
);
76+
}.bind(this));
77+
78+
return (
79+
<div>
80+
<ul>{list}</ul>
81+
<input type='text' onChange={this.updateValue} value={this.state.name}/>
82+
<button onClick={this.add}>Add</button>
83+
</div>
84+
)
85+
}
86+
87+
});
88+
89+
document.addEventListener('DOMContentLoaded', function () {
90+
React.renderComponent(<people></people>, document.getElementById('content'));
91+
});

package.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "react.io",
3+
"version": "0.0.0",
4+
"description": "Small experiment to see how easy it is to synchronize components using Socket.io.",
5+
"main": "index.js",
6+
"dependencies": {
7+
"browserify-middleware": "~1.19.0",
8+
"express": "~3.4.0",
9+
"lodash": "~2.2.1",
10+
"reactify": "~0.3.2",
11+
"socket.io": "~0.9.16",
12+
"react-tools": "~0.4.1"
13+
},
14+
"devDependencies": {},
15+
"author": "Geert Pasteels",
16+
"license": "BSD"
17+
}

react.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./node_modules/react-tools/build/modules/React');

readme.markdown

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# React + Socket.io
2+
3+
Small experiment to see how easy it is to synchronize components using Socket.io.
4+
5+
```shell
6+
git clone [email protected]:enome/react.io.git
7+
cd react.io
8+
npm install
9+
node server.js
10+
```
11+
12+
The main work here is done in the socket-mixin.js file which is a mixin you can include in your components. It will emit a change event when the state of a component changes. The server will then broadcast this event with the state to all components. Since this would update all the components the event also has a path which relates to the position of the component inside the dom.
13+
14+
In this example only the parent component (people) needs to sync it's state since it also sets the child components. If you do use the socketMixin in the person component you need to keep in mind that it will sync without telling the parent. So the state of the child component might differ from it's parent data.
15+
16+
If you load an other page after you already changed the data in the first page you might notice that they are out sync. This is because the same array gets initialized on each page load. Using a common data source (back-end data storage, indexedDB, ...) would fix this.

server.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
var http = require('http');
2+
var express = require('express');
3+
var browserify = require('browserify-middleware');
4+
var _ = require('lodash');
5+
6+
var app = express();
7+
8+
app.use(express.logger('dev'));
9+
10+
app.get('/build.js', browserify('./index.js', { transform: [ 'reactify'] }));
11+
12+
app.use(express.static(__dirname));
13+
14+
var server = http.createServer(app);
15+
16+
server.listen(3000, function () {
17+
console.log('3000');
18+
});
19+
20+
var io = require('socket.io').listen(server);
21+
22+
io.sockets.on('connection', function (socket) {
23+
24+
socket.on('component-change', function (data) {
25+
socket.broadcast.emit('component-change', data);
26+
});
27+
28+
});

socket-mixin.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
var _ = require('lodash');
2+
var utils = require('./utils');
3+
4+
var socketMixin = function (url) {
5+
6+
var socket = window.io.connect(url);
7+
8+
return {
9+
10+
changeHandler: function (data) {
11+
if (!_.isEqual(data.state, this.state) && this.path === data.path) {
12+
this.setState(data.state);
13+
}
14+
},
15+
16+
componentWillUpdate: function (props, state) {
17+
socket.emit('component-change', { path: this.path, state: state });
18+
},
19+
20+
componentDidMount: function (root) {
21+
this.path = utils.nodePath(root);
22+
socket.on('component-change', this.changeHandler);
23+
},
24+
25+
componentWillUnmount: function () {
26+
socket.removeListener('component-change', this.change);
27+
}
28+
29+
};
30+
31+
};
32+
33+
module.exports = socketMixin;

utils.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
var utils = {
2+
3+
indexInParent: function (node) {
4+
5+
var children = node.parentNode.childNodes;
6+
7+
for (var i = 0; i < children.length; i++) {
8+
if (children[i] === node) {
9+
return i;
10+
}
11+
}
12+
13+
return -1;
14+
15+
},
16+
17+
nodePath: function (e) {
18+
var path = [];
19+
20+
while (e !== document.body) {
21+
path.push(utils.indexInParent(e));
22+
e = e.parentNode;
23+
}
24+
25+
return path.reverse().join('-');
26+
27+
}
28+
29+
};
30+
31+
module.exports = utils;

0 commit comments

Comments
 (0)