React and Socket.io - node.js

I'm trying to create a simple app using ReactJS and Socket.io
In my component I want to be able to communicate with the server, but the problem is that I don't know how to do io.connect()
1.Do I need to explicitly specify the IP address like io.connect("http://myHost:7000") or is it enough to say : io.connect() ? As we can see in this piece of code :
https://github.com/DanialK/ReactJS-Realtime-Chat/blob/master/client/app.jsx
2.I do more or less the same as this code , but I receive error when I do npm start as io is undefined. I think , io is provided globally by including the socket.io script. How can I solve this problem ?
'use strict';
var React = require('react');
var socket = io.connect();
var chatWindow = React.createClass({
displayName: 'chatWindow',
propTypes: {},
getDefaultProps: function() {
return ({
messages: 0
});
},
componentDidMount: function() {
socket = this.props.io.connect();
socket.on('value', this._messageRecieve);
},
_messageRecieve: function(messages) {
this.setState({
messages: messages
});
},
getInitialState: function() {
return ({
messages: 0
});
},
_handleSend: function(){
var newValue = parseInt(this.refs.messageBox.value) + this.props.messages;
this.setState({
messages: newValue
});
socket.emit('clientMessage', { message: newValue});
},
render: function() {
var window =
<div>
<div>{this.props.messages}</div>
<input type="text" id="messageBox" refs="messageBox"></input>
<input type="button" onClick={this._handleSend} value="send" id="send"/>
</div>;
return (window);
}
});
module.exports = chatWindow;
This is the code :
https://github.com/arian-hosseinzadeh/simple-user-list

Answers:
1) No, you don't need to specify the IP, you can even use / and it will go through the default HTTP 80 port, anyway, you can find more examples on the socket.io site.
2) Require io too, remember to add socket.io-client to your package:
var React = require('react'),
io = require('socket.io-client');
Anyway, if you want to include the client script that socket.io server provides as a static file, then remember to add it into your HTML using a <script/> tag, that way you'll have io on the global scope avoiding the require part, but well, I prefer to require it.
NOW, WHAT ABOUT...
Trying my lib: https://www.npmjs.com/package/react-socket
It will handle the socket connection on mount and disconnection on unmount (the same goes for socket event listeners), give it a try and let me know.
Here you have an example:
http://coma.github.io/react-socket/
var App = React.createClass({
getInitialState: function() {
return {
tweets: []
};
},
onTweet: function(tweet) {
var tweets = this
.state
.tweets
.slice();
tweet.url = 'https://twitter.com/' + tweet.user + '/status/' + tweet.id;
tweet.at = new Date(tweet.at);
tweet.avatar = {
backgroundImage: 'url(' + tweet.img + ')'
};
tweets.unshift(tweet);
this.setState({
tweets: tweets
});
},
renderTweet: function (tweet) {
return (
<li key={tweet.id}>
<a href={tweet.url} target="_blank">
<div className="user">
<div className="avatar" style={ tweet.avatar }/>
<div className="name">{ tweet.user }</div>
</div>
<div className="text">{ tweet.text }</div>
</a>
</li>
);
},
render: function () {
return (
<div>
<ReactSocket.Socket url="http://tweets.socket.io"/>
<ReactSocket.Event name="tweet" callback={ this.onTweet }/>
<ul className="tweets">{ this.state.tweets.map(this.renderTweet) }</ul>
</div>
);
}
});
React.render(<App/>, document.body);

Related

Implementing a collaborative text editor using nodejs/react/socket but encountering problems because of slow heroku servers

I've tried making a collaborative editor using socket.io with reactjs frontend and node backend. Here's the piece of logic which I think is causing problems....
When a client starts typing on the editor, I've used onInput event to emit a socket response say "typing" which carries the complete text on the editor inside data object at the moment client presses a key. Now server catches this typing event and in response to that, emits another socket response called "typed" which contains the same data but the server sends this response to all the clients connected to the server.... Now all clients receive this event inside componentDidMount and then update the state variable "codeValue" which updates the editor content for all the clients.
There are two problems, first one that on one single typing event, server is emitting numerous typed events ( it happens only in heroku server and not on local host ) and the other problem is that heroku servers are slow and before the server sends response to update the state of clients, clients had already entered more text on the editor which simply vanishes when the state is updated.....
FRONTEND CODE:
import React from "react";
import { Dropdown } from "semantic-ui-react";
import languages from "../utils/languages";
//Styles
import "../styles/app.css";
//Editor
import * as ace from "ace-builds";
// import SocketIOClient from "socket.io-client";
import "ace-builds/src-noconflict/mode-c_cpp";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/ext-language_tools";
import AceEditor from "react-ace";
let check = true;
let ld;
// const endpoint = "http://localhost:4676";
// const socket = SocketIOClient(endpoint, { transports: ["websocket"] });
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
codeValue: languages[0].template,
currentLang: languages[0].key,
};
this.codeEditor = React.createRef();
this.fireTyping = this.fireTyping.bind(this);
this.onDDChange = this.onDDChange.bind(this);
this.runCode = this.runCode.bind(this);
this.handleOutput = this.handleOutput.bind(this);
}
componentDidMount() {
this.props.socket.on("typed", (data) => {
console.log(35, data.text)
this.setState({
codeValue: data.text,
});
check = true;
console.log(check)
});
this.props.socket.on('ans',(data) => {
console.log(data.output)
//handleOutput(data.output)
})
}
fireTyping = () => {
ld = this.codeEditor.current.editor.getValue()
//console.log(ld)
if(check) {
console.log(48, this.codeEditor.current.editor.getValue(), check);
this.props.socket.emit("typing", {
text: ld,
});
check = false;
}
console.log(check)
};
onDDChange = (e, data) => {
const selectedVal = languages.filter((v) => v.key == data.value)
this.setState({currentLang : data.value, codeValue: selectedVal[0].template})
}
runCode = () => {
this.props.socket.emit('run', {
code: this.codeEditor.current.editor.getValue(),
lang: this.state.currentLang,
input: ''
})
}
handleOutput = () => {
}
render() {
return (
<div>
<Dropdown
placeholder="Languages"
onChange = {this.onDDChange}
selection
value = {this.state.currentLang}
options={languages}
/>
<AceEditor
style={{
margin: "3rem auto",
width: "80vw",
height: "70vh",
}}
fontSize={18}
ref={this.codeEditor}
mode="c_cpp"
theme="github"
value={this.state.codeValue}
onInput={this.fireTyping}
showPrintMargin={false}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
setOptions={{
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
}}
/>
<div className="container">
<button
onClick={this.runCode}
style={{
marginLeft: "40rem",
}}
className="large ui teal button"
>
Run
</button>
</div>
</div>
);
}
}
export default App;
BACKEND CODE:
const express = require("express");
const request = require("request");
const app = express();
const http = require("http");
const server = http.createServer(app);
const path = require('path')
const socket = require("socket.io");
const io = socket(server);
const port = process.env.PORT || 4676
app.use(express.static(path.join(__dirname, 'client/build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/client/build/index.html'))
})
io.on("connection", (socket) => {
let previousCode, currentCode;
console.log(socket.id);
socket.on("typing", (data) => {
currentCode = data.text
console.log('typing')
console.log(previousCode === currentCode)
if(previousCode !== currentCode){
console.log(1)
io.emit("typed", data);
previousCode = currentCode;
currentCode = ''
}
});
});
server.listen(port, () => {
console.log("server started at http://localhost:4676");
});
I've spent hours trying to fix this but I couldn't.... Any help would be appreciated ☺️
Let me know if you need code reference, I'll share the repository

Same event emitting multiple times in socket.io-client reactjs

I am creating a chat app in react, expressjs and socket.io. When I click on Send Button, I am emitting an event and listening that event on server side and again emitting another event from server side and listening that event on client side. And I have written the event listening code on componentDidMount. But don't know why my client side calling same event multiple times. Below is my both side code:
Client side
var socketIOClient = require('socket.io-client')('http://localhost:4001');
sendMessageClicked(e) {
e.preventDefault();
let message = this.state.originalMessage;
var data = {
message: message,
time: Date.now()
}
socketIOClient.emit('send_message',data);
}
componentDidMount() {
socketIOClient.on('msg',function(result){
let messageHtml = 'messages working!';
let messageBox = document.getElementById('messageBox');
if (messageBox ) {
messageBox.appendChild(messageHtml);
}
})
}
render(){
return(
<div>
<form onSubmit={this.sendMessageClicked}>
<textarea onChange={this.handleMessageChange} name="originalMessage" value={this.state.originalMessage}></textarea>
<input type="submit" value="" />
</form>
</div>
);
}
Server side
const app = require('./app');
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(4001);
io.on('connection',function(socket){
socket.on('send_message',function(data){
io.emit('msg',data);
})
})
Can anyone please help with the same?
I had the same issue, and I solved it with useEffect hook.
in your case it would be (on client side):
useEffect(()=>{
socket.on('msg', (result) => {
let messageHtml = 'messages working!';
let messageBox = document.getElementById('messageBox');
if (messageBox ) {
messageBox.appendChild(messageHtml);
})
return function cleanup() {socket.off('msg')}
},[])
I'm sure you could do this also with ComponentDidUpdate or ComponentDidUnmount, but useEffect is eassier.
In the useEffect() hook, checking if socket connection already exists using socket.current helped me get rid of this problem -
useEffect(() => {
(async () => {
if (!socket.current) {
socket.current = io(`http://localhost:8080/`)
.on("connect", () => {
//do something
});
socket.current.on("message", (instance) => {
//receive message from server
});
}
})();
});

How to show only one item in Angular2, Ionic2

Please bear with my question as I'm new to Angular and I don't know exactly where the problem is, I've tried to google for a solution but with no good luck, basically I'm running a simple NodeJS server with socket.io:
SERVER
var socket = require('socket.io'),
http = require('http'),
server = http.createServer(),
socket = socket.listen(server);
var msgProva = 2;
socket.on('connection', function(connection) {
console.log('User Connected');
socket.emit('prova', msgProva);
connection.on('message', function(msg){
socket.emit('message', msg);
});
connection.on('sValue', function(value){
console.log('sValue', value );
socket.emit('sValue', value);
});
});
server.listen(3000, function(){
console.log('Server started');
});
and my client TS code:
HOME.TS
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import * as io from 'socket.io-client';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
socket:any
chat_input:string;
chats = [];
prova = [];
slideValueBadge = [];
constructor(public navCtrl: NavController) {
var a;
this.socket = io('http://localhost:3000');
this.socket.on('message', (msg) => {
console.log("message", msg);
this.chats.push(msg);
});
this.socket.on('prova', (msgProva) => {
console.log("msgProva", msgProva);
this.prova.push(msgProva);
});
this.socket.on('sValue', (value) => {
let index: number = this.slideValueBadge.indexOf(value);
if (index != -1) {
a = this.slideValueBadge.splice(index, 1);
}
console.log("sliderValue:", value);
});
}
send(msg) {
if(msg != ''){
this.socket.emit('message', msg);
}
this.chat_input = '';
}
cValue(event, nome) {
console.log("SliderValue", event._valA);
this.socket.emit('sValue', event._valA);
}
}
HOME.HTML
<ion-content padding>
<ion-item>
<ion-range [(ngModel)]="brightness" (ionChange)="cValue($event, 'slider1')">
<ion-icon range-left small name="water"></ion-icon>
<ion-icon range-right name="water"></ion-icon>
</ion-range>
</ion-item>
Flusso d'acqua
<ion-badge color="secondary">{{slideValueBadge}}</ion-badge>
<ion-list>
<ion-item *ngFor="let message of chats">{{message}}</ion-item>
</ion-list>
<ion-item>
<ion-input type="text" [(ngModel)]="chat_input" placeholder="Enter message"></ion-input>
</ion-item>
<button ion-button block (click)="send(chat_input)">Send</button>
</ion-content>
And that is what I see as result:
How can I display only one value? (the last one)?
Basically you are binding your badge to an array (slideValueBadge = []).
Simply change the binded value to a number and then update it in your socket.on method.
Hope this could help you
Apparently this was the solution, dunno if is good or bad coding:
<ion-badge color="secondary">{{slideValueBadge[slideValueBadge.length -1]}}</ion-badge>
without the *ng-if

componentDidMount is not triggered

Hi Im new to React & nodeJS, i'm trying to call my nodejs api through react and my componentDidMount is not triggered at all even after rendering the page.
Can some one please give some idea on where exactly I might be missing.
var React = require('react');
module.exports = React.createClass({
getInitialState: function() {
return {
jobs: []
}
},
componentDidMount: function() {
console.log("mount");
var _this = this;
this.serverRequest = $.ajax({
....
}.bind(this)
});
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
return (
<div><h1>Jobs</h1>
{this.state.jobs.map(function(job) {
return (
<div key={job.id} className="job">
{job.name}
{job.address}
</div>
);
})}
</div>
)
}
});
In my NodeServer.js file i'm calling in this way, Only Jobs is being displayed in my html page
app.get('/', function(request, response) {
var htmlCode = ReactDOMServer.renderToString(React.createElement(Component));
response.send(htmlCode);
})
You can't use the React component lifecycle methods to load data when you're rendering in the backend, this will only work in the client. When you render the component on the server, it is only rendered once, without the data. The data is loaded asynchronously, when it is finished, your component has already been rendered. You have to fetch your data outside the component, then pass it to the component as a prop.

ReactJS - Creating children components by looping through object

I have parent and child component. I want the parent to render multiple child components with properties specified in an object. I cannot seem to make the loop in the render function work.
var Inputs = React.createClass({
propTypes: {
type: React.PropTypes.string,
xmltag: React.PropTypes.string,
class: React.PropTypes.string
},
getDefaultProps: function () {
return {
type: ' text'
};
},
render: function() {
return (
<div className={'form-element col-xs-6 col-sm-6 ' + this.props.class}>
<label className="col-xs-12">{this.props.text}</label>
<input className="col-xs-12" type={this.props.type} xmltag={this.props.xmltag}></input>
</div>
);
},
});
//OBJECT that needs to be rendered
var formTags = {
id: ["ID", "List ID", "text"],
title: ["TITLE", "List Title", "text"],
said: ["SAID", "SAID", "number"]
};
var InputList = React.createClass({
//PROBLEM STARTS HERE
render: function() {
for (var key in formTags) {
return (
//Not using everything from formTags
<Inputs type="number" text={key}>
);
};
},
//PROBLEM ENDS HERE
});
ReactDOM.render(<InputList />, document.getElementById('mainForm'));
React component must have only one root element, now you are trying render several elements, add one root element, like in example (you can use any elements <div> <p> etc.)
var InputList = React.createClass({
render: function() {
var inputs = Object.keys(this.props.tags).map(function (key) {
return <Inputs key={ key } type="number" text={ key } />;
});
return <div>
{ inputs }
</div>;
}
});
Example

Resources