You have a choice from where you'd like to start.
- On your own, do whatever client type you want and make your own project/folder from scratch.
- Clone this repo and begin with one of the provided apps.
- NativeClientStarter - React Native (No redux, only a component example)
- WebClientStarter - React (No redux, only a component example)
- NativeClient - React Native + Redux (Complete, working implementation)
- SocketIO Chat Example - Ignore the server part.
- React Tutorial - Super basic intro
- Redux Sample Todo - Can see all the parts on one page
- Example Redux + React App - React Tic-Tac-Toe App, made at a previous meetup
git clone https://github.com/jscodingnights/chat-client.git
cd chat-client
cd STARTER_FOLDER_NAME
npm install
Complete the Requirements in the Getting Started guide.
From the web folder, run npm run dev and navigate to http://localhost:8080/webpack-dev-server/index.html.
Chat Server URL: https://jscn-chat.herokuapp.com
When connecting via Socket.IO, you can leave off the "https://" bit.
See note about alternative action event syntax below.
Using the URL above, establish a socket connection.
// Be sure to use require, as import is hoisted. The if needs to come first as well. Just... don't change these lines... :)
if (!window.navigator.userAgent) {
// Fix socket.io check
window.navigator.userAgent = 'react-native';
}
const io = require('socket.io-client/socket.io');
const socket = io('jscn-chat.herokuapp.com', { jsonp: false, transports: ['websocket'] });import io from 'socket.io-client';
const socket = io('jscn-chat.herokuapp.com');socket has two methods of importance. emit and on which send and listen to events respectively. When any event is dispatched corresponding to the event name given to on, the callback is executed with the included payload.
When you first connect, your username is set to "Anonymous". This can be changed (see Step 4).
const messageEventData = {
message: {
text: 'Hello World!'
}
};
socket.emit('CREATE_MESSAGE', messageEventData);IMPORTANT When you emit a
CREATE_MESSAGEevent, you will not receive the correspondingRECEIVE_MESSAGEevent. That is, a client is responsible for displaying its own messages and need not wait for a socket response before doing so.
const appState = {
messages: []
};
socket.on('RECEIVE_MESSAGE', (incomingMessage) => {
// incomingMessage ==> {
// type: 'RECEIVE_MESSAGE',
// message: {
// text: 'Message body text',
// author: {
// username: 'User Name'
// },
// date: 1459693594853 // Epoch
// }
// }
appState.messages.push(incomingMessage.message);
});When you first connect, you are assigned a username of "Anonymous". You can change this using the event UPDATE_USER so that your name is sent along with any messages you create.
When you successfully update your user, an event will be sent back of the type RECEIVE_USER to provide the full state of the user that the server has. You can use this to sync your local user state with that of the server.
const userData = {
user: {
username: 'Sally Sue'
}
};
// Receive the server's state of your current user info
socket.on('RECEIVE_USER', (incomingMessage) => {
// incomingMessage ==> {
// type: 'RECEIVE_USER',
// user: {
// username: 'Sally Sue',
// created: 1459693594853 // Epoch
// }
// }
// Update local user state
userData.user = incomingMessage.user;
});
// Give the server an update of your user details
socket.emit('UPDATE_USER', userData);When you first connect, and whenever a user joins, leaves, or changes their profile data, the entire list of active users is broadcasted via RECEIVE_MEMBERS. This is heavy-handed obviously, but a nice simplification. Rather than trying to make individual user updates locally, simply discard your entire local state of users and update with the state given.
const appState = {
members: []
};
socket.on('RECEIVE_MEMBERS', (incomingMessage) => {
// incomingMessage ==> {
// type: 'RECEIVE_MEMBERS',
// members: [{
// username: 'Anonymous'
// },{
// username: 'Sally Sue',
// created: 1459693594853 // Epoch
// }]
// }
// Blast away current state and re-render
appState.members = incomingMessage.members;
});
All events can also be listened to, or emitted, via a unified action event where the payload contains an object with a type property corresponding to the event type.
This is because Socket.IO does not support wildcard matching, and if you wanted to have a single event handler receive every event, that'd be otherwise not possible without a plugin.
This is choice also makes the use of Redux-socket.io possible if you choose.
// Receive all action types
socket.on('action', (incomingMessage) => {
switch (incomingMessage.type) {
case 'RECEIVE_USER':
// ...
break;
// ...
}
});
// Single emit example using 'action'
socket.emit('action', {
type: 'CREATE_MESSAGE',
message: {
text: 'Hello world!'
}
});