├── .gitignore
├── README.md
├── app.js
├── client
└── app.jsx
├── package.json
├── public
├── css
│ └── style.css
├── index.html
└── js
│ └── app.js
└── routes
└── socket.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ReactJS Socket.io Chat Application
2 |
3 | See This Blog Post : [ReactJS and Socket.IO Chat Application](http://danialk.github.io/blog/2013/06/16/reactjs-and-socket-dot-io-chat-application/)
4 |
5 | ## Running it
6 |
7 | First, grab the dependencies:
8 |
9 | npm install
10 |
11 | Build the applicaiton
12 |
13 | npm run build
14 |
15 | Then run the app like so:
16 |
17 | npm start
18 |
19 | And navigate to `localhost:3000` and chat !
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var express = require('express');
8 | var http = require('http');
9 |
10 | var socket = require('./routes/socket.js');
11 |
12 | var app = express();
13 | var server = http.createServer(app);
14 |
15 | /* Configuration */
16 | app.set('views', __dirname + '/views');
17 | app.use(express.static(__dirname + '/public'));
18 | app.set('port', 3000);
19 |
20 | if (process.env.NODE_ENV === 'development') {
21 | app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
22 | }
23 |
24 | /* Socket.io Communication */
25 | var io = require('socket.io').listen(server);
26 | io.sockets.on('connection', socket);
27 |
28 | /* Start server */
29 | server.listen(app.get('port'), function (){
30 | console.log('Express server listening on port %d in %s mode', app.get('port'), app.get('env'));
31 | });
32 |
33 | module.exports = app;
34 |
--------------------------------------------------------------------------------
/client/app.jsx:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 |
5 | var socket = io.connect();
6 |
7 | var UsersList = React.createClass({
8 | render() {
9 | return (
10 |
11 |
Online Users
12 |
13 | {
14 | this.props.users.map((user, i) => {
15 | return (
16 | -
17 | {user}
18 |
19 | );
20 | })
21 | }
22 |
23 |
24 | );
25 | }
26 | });
27 |
28 | var Message = React.createClass({
29 | render() {
30 | return (
31 |
32 | {this.props.user} :
33 | {this.props.text}
34 |
35 | );
36 | }
37 | });
38 |
39 | var MessageList = React.createClass({
40 | render() {
41 | return (
42 |
43 |
Conversation:
44 | {
45 | this.props.messages.map((message, i) => {
46 | return (
47 |
52 | );
53 | })
54 | }
55 |
56 | );
57 | }
58 | });
59 |
60 | var MessageForm = React.createClass({
61 |
62 | getInitialState() {
63 | return {text: ''};
64 | },
65 |
66 | handleSubmit(e) {
67 | e.preventDefault();
68 | var message = {
69 | user : this.props.user,
70 | text : this.state.text
71 | }
72 | this.props.onMessageSubmit(message);
73 | this.setState({ text: '' });
74 | },
75 |
76 | changeHandler(e) {
77 | this.setState({ text : e.target.value });
78 | },
79 |
80 | render() {
81 | return(
82 |
83 |
Write New Message
84 |
90 |
91 | );
92 | }
93 | });
94 |
95 | var ChangeNameForm = React.createClass({
96 | getInitialState() {
97 | return {newName: ''};
98 | },
99 |
100 | onKey(e) {
101 | this.setState({ newName : e.target.value });
102 | },
103 |
104 | handleSubmit(e) {
105 | e.preventDefault();
106 | var newName = this.state.newName;
107 | this.props.onChangeName(newName);
108 | this.setState({ newName: '' });
109 | },
110 |
111 | render() {
112 | return(
113 |
114 |
Change Name
115 |
121 |
122 | );
123 | }
124 | });
125 |
126 | var ChatApp = React.createClass({
127 |
128 | getInitialState() {
129 | return {users: [], messages:[], text: ''};
130 | },
131 |
132 | componentDidMount() {
133 | socket.on('init', this._initialize);
134 | socket.on('send:message', this._messageRecieve);
135 | socket.on('user:join', this._userJoined);
136 | socket.on('user:left', this._userLeft);
137 | socket.on('change:name', this._userChangedName);
138 | },
139 |
140 | _initialize(data) {
141 | var {users, name} = data;
142 | this.setState({users, user: name});
143 | },
144 |
145 | _messageRecieve(message) {
146 | var {messages} = this.state;
147 | messages.push(message);
148 | this.setState({messages});
149 | },
150 |
151 | _userJoined(data) {
152 | var {users, messages} = this.state;
153 | var {name} = data;
154 | users.push(name);
155 | messages.push({
156 | user: 'APPLICATION BOT',
157 | text : name +' Joined'
158 | });
159 | this.setState({users, messages});
160 | },
161 |
162 | _userLeft(data) {
163 | var {users, messages} = this.state;
164 | var {name} = data;
165 | var index = users.indexOf(name);
166 | users.splice(index, 1);
167 | messages.push({
168 | user: 'APPLICATION BOT',
169 | text : name +' Left'
170 | });
171 | this.setState({users, messages});
172 | },
173 |
174 | _userChangedName(data) {
175 | var {oldName, newName} = data;
176 | var {users, messages} = this.state;
177 | var index = users.indexOf(oldName);
178 | users.splice(index, 1, newName);
179 | messages.push({
180 | user: 'APPLICATION BOT',
181 | text : 'Change Name : ' + oldName + ' ==> '+ newName
182 | });
183 | this.setState({users, messages});
184 | },
185 |
186 | handleMessageSubmit(message) {
187 | var {messages} = this.state;
188 | messages.push(message);
189 | this.setState({messages});
190 | socket.emit('send:message', message);
191 | },
192 |
193 | handleChangeName(newName) {
194 | var oldName = this.state.user;
195 | socket.emit('change:name', { name : newName}, (result) => {
196 | if(!result) {
197 | return alert('There was an error changing your name');
198 | }
199 | var {users} = this.state;
200 | var index = users.indexOf(oldName);
201 | users.splice(index, 1, newName);
202 | this.setState({users, user: newName});
203 | });
204 | },
205 |
206 | render() {
207 | return (
208 |
209 |
212 |
215 |
219 |
222 |
223 | );
224 | }
225 | });
226 |
227 | React.render(, document.getElementById('app'));
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "reactjs-socket-io-chat-app",
3 | "version": "0.6.9",
4 | "private": true,
5 | "scripts": {
6 | "build": "browserify ./client/app.jsx -t babelify --outfile ./public/js/app.js",
7 | "start": "node app.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.13.1",
11 | "react": "^0.13.3",
12 | "socket.io": "^1.3.5"
13 | },
14 | "devDependencies": {
15 | "babelify": "^6.1.3",
16 | "browserify": "^10.2.6"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | width: 600px;
3 | margin: 0 auto;
4 | border: 5px solid tomato;
5 | }
6 | .users{
7 | float : right;
8 | margin-right: 1.5em;
9 | }
10 | .messages {
11 | width: 400px;
12 | margin: 1em;
13 | border : 1px solid black;
14 | padding: 1em;
15 | }
16 | .message_form,
17 | .change_name_form{
18 | padding: 1em;
19 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ReactJS Socket.io Chat Application
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/routes/socket.js:
--------------------------------------------------------------------------------
1 | // Keep track of which names are used so that there are no duplicates
2 | var userNames = (function () {
3 | var names = {};
4 |
5 | var claim = function (name) {
6 | if (!name || names[name]) {
7 | return false;
8 | } else {
9 | names[name] = true;
10 | return true;
11 | }
12 | };
13 |
14 | // find the lowest unused "guest" name and claim it
15 | var getGuestName = function () {
16 | var name,
17 | nextUserId = 1;
18 |
19 | do {
20 | name = 'Guest ' + nextUserId;
21 | nextUserId += 1;
22 | } while (!claim(name));
23 |
24 | return name;
25 | };
26 |
27 | // serialize claimed names as an array
28 | var get = function () {
29 | var res = [];
30 | for (user in names) {
31 | res.push(user);
32 | }
33 |
34 | return res;
35 | };
36 |
37 | var free = function (name) {
38 | if (names[name]) {
39 | delete names[name];
40 | }
41 | };
42 |
43 | return {
44 | claim: claim,
45 | free: free,
46 | get: get,
47 | getGuestName: getGuestName
48 | };
49 | }());
50 |
51 | // export function for listening to the socket
52 | module.exports = function (socket) {
53 | var name = userNames.getGuestName();
54 |
55 | // send the new user their name and a list of users
56 | socket.emit('init', {
57 | name: name,
58 | users: userNames.get()
59 | });
60 |
61 | // notify other clients that a new user has joined
62 | socket.broadcast.emit('user:join', {
63 | name: name
64 | });
65 |
66 | // broadcast a user's message to other users
67 | socket.on('send:message', function (data) {
68 | socket.broadcast.emit('send:message', {
69 | user: name,
70 | text: data.text
71 | });
72 | });
73 |
74 | // validate a user's name change, and broadcast it on success
75 | socket.on('change:name', function (data, fn) {
76 | if (userNames.claim(data.name)) {
77 | var oldName = name;
78 | userNames.free(oldName);
79 |
80 | name = data.name;
81 |
82 | socket.broadcast.emit('change:name', {
83 | oldName: oldName,
84 | newName: name
85 | });
86 |
87 | fn(true);
88 | } else {
89 | fn(false);
90 | }
91 | });
92 |
93 | // clean up when a user leaves, and broadcast it to other users
94 | socket.on('disconnect', function () {
95 | socket.broadcast.emit('user:left', {
96 | name: name
97 | });
98 | userNames.free(name);
99 | });
100 | };
101 |
--------------------------------------------------------------------------------