React + Nodejs/Express App to display products - node.js

I've got a simple React App with Nodejs/Express server.
Right now I'm trying to obtain an ID value from Serialport and compare that ID with the ID from an external webservice and retrieve the data I get from the webservice and display it on the frontend.
I've got the first part working and right now I can compare both ID's and fetch the data but can't figure out how to display it in the React frontend.
This is my server index.js:
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname + "/public")));
const PORT = process.env.PORT || 5000;
const scanProducts = () => {
const scan = require('./actions/scanProduct');
scan.scanner();
//can't reach data here
}
app.listen(PORT);
scanProducts();
This is my scanProduct.js
const { SerialPort } = require('serialport');
module.exports = {
scanner: function(){
const port = new SerialPort({ path: 'COM6', baudRate: 9600, autoOpen: false });
port.open(function (err) {
if (err) {
return console.log('Error opening port: ', err.message)
}
port.write('main screen turn on')
})
port.on('open', function() {
setInterval(function(){
const portReader = port.read();
if(portReader != null){
const sensorVal = Buffer.from(portReader).toString();
const soap = require('soap');
const url = 'http://example.com?wsdl';
soap.createClient(url, function(err, client) {
client.GetProductById({ UserId: "1", ProductId: sensorVal }, function(err, result) {
if(err) return console.log(err);
console.log(result.GetProductByIdResult);
return result.GetProductByIdResult; //returns nothing into index.js
});
});
}
}, 500);
})
}
}
Then I would like to add each product from the server into this custom component, right now it displays static data fetched from a basic json file.
This my component ProductsList.js:
import ProductRow from "./ProductRow";
import './ProductsList.css';
const ProductsList = props => {
return(
<>
<div id="products-wrapper" className="w-9/12 h-full px-20 py-20 flex flex-col overflow-x-hidden overflow-y-scroll ">
<div className="w-full h-auto my-2 px-3 py-3 font-semibold grid grid-cols-3 bg-blue-600 rounded-xl">
<div className="w-[60%] text-left">Produto</div>
<div className="w-[20%] text-left">Quant.</div>
<div className="w-[20%] text-left">Preço</div>
</div>
{props.items.map(product => <ProductRow name={product.name} quantity={product.quantity} price={product.price}/>)}
//here is where I need to add each product from the server
</div>
</>
);
}
export default ProductsList;
Any idea what I might be missing?
Any help would be greatly appreciated.

A sample code for your project
I put some comments, I hope it will be easy to follow
I wrote some remarks at the end of the code too
Backend
For the index.js (main) file of your backend
const EventEmitter = require('events');
var cors = require('cors')
// Init express app
const app = require('express')();
// Import your scanner utility
const scan=require('./scanProduct')
/*Enabling CORS for any origin (host and port) to test the service from a another origin (react client) .
It depends how you deploy your services */
app.use(cors({
origin:'*'
}))
// Init the http server
const server = require('http').createServer(app);
// Init the websocket library
const io = require('socket.io')(server);
// Define some plain emitter
const emitter=new EventEmitter()
// Pass the event emitter in it
scan.scanner(emitter)
// Event fired when the websocket has been established
io.on('connection', socket => {
// When the scanProduct fire an event on scanner channel
emitter.on("scanner",function(data){
// Emit the data on the websocket for the react client
// You can define whatever name you prefer, here also scanner
socket.emit('scanner',data)
})
});
//Listening on port 8080
server.listen(8080);
And for the scanProduct.js file i just "mocked" your implementation with setInterval every 2 secs in my case
module.exports = {
scanner: function(eventEmitter){
// Read serial port every 2000ms
setInterval(()=>{
/*
- Read on serial port
- fetch the data from the soap web service
*/
// just some data to send but should be from your workflow
const productResult=new Date().toISOString()
// you emit on the channel you defined in the index.js file
eventEmitter.emit("scanner",productResult)
},2000)
}
}
The depedencies I am using on the backend
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"socket.io": "^4.6.0"
}
Frontend
For the front-end, I used npx create-react-app to init the project,
And I edited only one file, the App.js file
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client';
// Websocket initialization, use your config or .env file to set up the url
const websocket = io(
"http://localhost:8080",
{ transports: ['websocket'] }
)
function App() {
//State keeping all the products we are displaying
const [products, setProducts] = useState([])
//When App component is mounted
useEffect(() => {
// Just to log the connection
websocket.on('connection', () => {
console.log("Websocket connected")
})
// When websocket receive data on the scanner channel, emitted from the backend
websocket.on("scanner", (product) => {
// Update the products with new ones
setProducts(prevProducts => [...prevProducts, product])
})
}, [])
return (
<div>
<div>
Number of products : {products.length}
</div>
{
products.map((data) => {
return (
<div>
{data}
</div>
)
})
}
</div>
);
}
export default App;
My sample code as it is will send data every 2000ms and we will have en enormous list in no time ^^'.
For your use case, I do not know if it is relevant to send data every 500ms to the client.
Do you have continuous flow of valuable/usefull data on the serial port ? If you really have valuable data every 500ms, another approach would be more appropriate, like batching. The idea will be to save somewhere the products you collected every 500ms, and from titme to time, every 10 secondes for example, you send this list of new products to the client.
If the payload on the serial port arrives randomly, you need to build some logic into your setInterval function, and only trigger an event when necessary.
The sample of course lacks some features. You only have new products coming from the websocket, but no history. If you refresh your browser page or if the socket is closed for whatever reason, you are going to miss some products. If history is important, you need some persistence layer.

Related

Next.js (React.js), with Node.js server Socket.io client multiple IDs

I'm having an issue where my React app is outputting the same one ID on every page that it loads, where as Backend Node.js Socket.io server is outputting multiple client IDs whenever I change route in my app...
Server:
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:3000",
},
});
io.on("connection", (socket) => {
console.log("client connected: ", socket.id);
socket.on("disconnect", (reason) => {
console.log("disconnect", reason);
});
});
App.js:
useEffect(() => {
console.log("use effect", socket.id);
return () => {
socket.off("connect");
socket.off("disconnect");
};
}, []);
Socket.ts:
import io from "socket.io-client";
const socket = io(`${process.env.NEXT_PUBLIC_SOCKET_IO_URL}`);
socket.on("connect", () => console.log("socket_id", socket.id));
export default socket;
server.js (backend - websocket)
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:3000",
},
});
io.on("connection", (socket) => {
console.log("client connected: ", socket.id);
socket.on("disconnect", (reason) => {
console.log("disconnect", reason);
});});
First of all, socket.io server sometimes generates a new id due reconnections or others things:
https://socket.io/docs/v4/server-socket-instance/#socketid
So if you are planning keep the same id, i disencourage you.
Well, but looks you are wondering about fast recriation of ids. React is rendering according state changes, so create a stateless connection inside some component cause this behaviour. Yon can choose a lot of solutions but, i will present you a solution extensible, ease to mantain and deliver to componets the role of subscribe and unsubscribe to itself listeners, this sounds better than have a global listeners declaration :D
1. Create Socket Context
We will use useContext hook to provide SocketContext to entire app.
Create a file in context/socket.js:
import React from "react"
import socketio from "socket.io-client"
import { SOCKET_URL } from "config"
export const socket = socketio.connect(SOCKET_URL)
export const SocketContext = React.createContext()
2. Use socket context and provide a value
Add SocketContext provider at the root of your project or at the largest scope where socket is used:
import {SocketContext, socket} from 'context/socket';
import Child from 'components/Child';
const App = () => {
return (
<SocketContext.Provider value={socket}>
<Child />
<Child />
...
</SocketContext.Provider
);
};
3. Now you can use socket in any child component
For example, in GrandChild component, you can use socket like this:
import React, {useState, useContext, useCallback, useEffect} from 'react';
import {SocketContext} from 'context/socket';
const GrandChild = ({userId}) => {
const socket = useContext(SocketContext);
const [joined, setJoined] = useState(false);
const handleInviteAccepted = useCallback(() => {
setJoined(true);
}, []);
const handleJoinChat = useCallback(() => {
socket.emit("SEND_JOIN_REQUEST");
}, []);
useEffect(() => {
// as soon as the component is mounted, do the following tasks:
// emit USER_ONLINE event
socket.emit("USER_ONLINE", userId);
// subscribe to socket events
socket.on("JOIN_REQUEST_ACCEPTED", handleInviteAccepted);
return () => {
// before the component is destroyed
// unbind all event handlers used in this component
socket.off("JOIN_REQUEST_ACCEPTED", handleInviteAccepted);
};
}, [socket, userId, handleInviteAccepted]);
return (
<div>
{ joined ? (
<p>Click the button to send a request to join chat!</p>
) : (
<p>Congratulations! You are accepted to join chat!</p>
) }
<button onClick={handleJoinChat}>
Join Chat
</button>
</div>
);
};
What is useContext?
useContext provides a React way to use global state,
You can use context in any child component,
Context values are states. React notices their change and triggers re-render.
What is useCallback? Why did you put every handlers inside useCallback?
useCallback prevents reassigning whenever there is state update
Functions will be reassigned only when elements in the second argument are updated
More reference:
https://www.w3schools.com/react/react_usecallback.asp
This nice tutorial has obtained in:
https://dev.to/bravemaster619/how-to-use-socket-io-client-correctly-in-react-app-o65

SSE - Server Sent Events - How to reuse same EventSource

I'm new in programming and trying to understand SSE. I'm using React for frontend , Node.js for backend and MongoDB for database. Sorry for my english in advance.
When i open the website, creating eventsource and start to listen backend "/test". I have a button in frontend and when i click it , a random float number between 0 and 1 posted backend "/savedata". Also showing numbers bigger than 0.5.
In server side for "/test", checking database for the new records every 3 seconds and if recorded numbers is bigger than 0.5, send it to frontend and delete the database record.
For "/savedata", saving numbers to database coming from frontend.
My Question is;
when i open website in a new tab, another eventsource is being created and trigger database "/test". Is there a way to reuse eventsource instead of create a new eventsource ? Also if you have any suggestions for my code, pls tell me. i'm trying to learn.
Here is my code;
Frontend React - FrontendTest.js
import axios from 'axios'
import React, { useEffect, useState } from 'react'
const FrontendTest = () => {
const [data, setData] = useState(null)
const [databaseInfo, setDatabaseInfo] = useState(null)
let number = 0
const url = 'http://localhost:5000/test'
let source
useEffect(() => {
source = new EventSource(url)
source.onmessage = (e) => {
setData(JSON.parse(e.data))
}
}, [])
const buttonClicked = async (e) => {
e.preventDefault()
number = Math.random()
const sendReq = await
axios.post('http://localhost:5000/savedata', {
number,
})
setDatabaseInfo(sendReq.data)
}
return (
<div>
<div>
<button onClick={buttonClicked}>Send</button>
<p>{`If the number > 0.5 , it will be founded`}</p>
<p>
{databaseInfo &&
`${databaseInfo.data.toFixed(4)} Saved to
Database !`}
</p>
<p>
{data && `${data.toFixed(4)} Founded ! Database
input deleted ! `}
</p>
</div>
</div>
)
}
Node.js - server.js
import express from 'express'
import cors from 'cors'
import expressAsyncHandler from 'express-async-handler'
import mongoose from 'mongoose'
import Datas from './model.js'
const app = express()
const port = 5000
app.use(
cors({
origin: 'http://localhost:3000',
credentials: true,
})
)
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
let interval
app.post(
'/savedata',
expressAsyncHandler(async (req, res) => {
const data = req.body.number
const result = await Datas.create({
data1: data,
})
res.send({ data })
})
)
app.get(
'/test',
expressAsyncHandler(async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
clearInterval(interval)
interval = setInterval(async () => {
const database = await Datas.find({})
const databaseData1 = database.map((item) => item.data1)
const databaseIds = database.map((item) => item._id)
const data = {
value: databaseData1,
}
for (let i = 0; i < data.value.length; i++) {
if (data.value[i] > 0.5) {
console.log(data.value[i])
res.write(`data: ${JSON.stringify(data.value[i])}\n\n`)
await Datas.findByIdAndDelete(databaseIds[i])
}
}
console.log('Searching')
}, 3000)
})
)
mongoose
.connect(CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() =>
app.listen(port, () =>
console.log(`Example app listening at
http://localhost:${port}`)
)
)
.catch((error) => console.log(error))
You can't directly share the event source handle, but what you are able to do is share data between tabs, when they are in the same origin.
One approach would be LocalStorage. So the first tab would write to the storage that it is going to run the EventSource connection. The second tab that connects would see there is already another tab open, and start listening. Then each time an event comes into the first tab, it writes it to local storage, and the second tab can see it.
(You need to handle the case of what happens if the first tab closes; especially if there are multiple other tabs listening in, so be aware that making this production-ready is going to get quite complicated.)
Another WebAPI that is specifically for doing that kind of thing is Broadcast Channel. I've not used it: the browser support is almost the same as EventSource, but Safari is lagging behind.
It looks like this question was created to keep track of the various approaches.

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

Node/React/Redux: having problems passing api JSON object between Node and React

I am new to React/redux with Node. I am working on a full stack app that utilizes Node.js on the server side and React/Redux on the client side. One of the functions of the app is to provide a current and eight-day weather forecast for the local area. The Weather route is selected from a menu selection on the client side that menu selection corresponds to a server side route that performs an axios.get that reaches out and consumes the weather api (in this case Darksky) and passes back that portion of the JSON api object pertaining to the current weather conditions and the eight-day weather forecast. There is more to the API JSON object but the app consume the "current" and "daily" segment of the total JSON object.
I have written a stand-alone version of the server-side axios "get" that successfully reaches out to the Darksky API and returns the data I am seeking. I am, therefore, reasonably confident my code will correctly bring back the data that I need. My problem consists in this: when I try to render the data in my React Component, the forecast object is undefined. That, of course, means there is nothing to render.
I have reviewed my code, read a plethora of documentation and even walked through tutorials that should help me find the problem and it still eludes me. So, I am stuck and would greatly appreciate some help. Most of the comment you still in the code below will be removed after the debugging process is completed.
I am including code blocks relevant to the problem:
My React Component
// client/src/components/pages/functional/Weather.js
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import Moment from 'react-moment';
import Spinner from '../../helpers/Spinner'
import { getWeather } from '../../../redux/actions/weather'
const Weather = ({ getWeather, weather: { forecast, loading } }) => {
// upon load - execute useEffect() only once -- loads forecast into state
useEffect(() => { getWeather(); }, [getWeather])
return (
<div id='page-container'>
<div id='content-wrap' className='Weather'>
{ loading ?
<Spinner /> :
<>
<div className='WeatherHead box mt-3'>
<h4 className='report-head'>Weather Report</h4>
</div>
{/* Current Weather Conditions */}
<h6 className='current-head'>Current Conditions</h6>
<section className='CurrentlyGrid box mt-3'>
/* additional rendering code removed for brevity */
<span><Moment parse='HH:mm'>`${forecast.currently.time}`</Moment></span>
/* additional rendering code removed for brevity */
</section>
</>
}
</div>
</div>
);
};
Weather.propTypes = {
getWeather: PropTypes.func.isRequired,
weather: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ forecast: state.forecast });
export default connect( mapStateToProps, { getWeather } )(Weather);
My React Action Creator
// client/src/redux/actions/weather.js
import axios from 'axios';
import chalk from 'chalk';
// local modules
import {
GET_FORECAST,
FORECAST_ERROR
} from './types';
// Action Creator
export const getWeather = () => async dispatch => {
try {
// get weather forecast
const res = await axios.get(`/api/weather`);
console.log(chalk.yellow('ACTION CREATOR getWeather ', res));
// SUCCESS - set the action -- type = GET_WEATHER & payload = res.data (the forecast)
dispatch({
type: GET_FORECAST,
payload: res.data
});
} catch (err) {
// FAIL - set the action FORECAST_ERROR, no payload to pass
console.log('FORECAST_ERROR ',err)
dispatch({
type: FORECAST_ERROR
});
};
};
My React Reducer
// client/src/redux/reducers/weather.js
import {
GET_FORECAST,
FORECAST_ERROR,
} from '../actions/types'
const initialState = {
forecast: null,
loading: true
}
export default (state = initialState, action) => {
const { type, payload } = action
switch (type) {
case GET_FORECAST:
return {
...state,
forecast: payload,
loading: false
}
case FORECAST_ERROR:
return {
...state,
forecast: null,
loading: false
}
default:
return state
}
}
My Node Route
// server/routes/api/weather.js
const express = require('express');
const axios = require('axios');
const chalk = require('chalk');
const router = express.Router();
// ***** route: GET to /api/weather
router.get('/weather', async (req, res) => {
try {
// build url to weather api
const keys = require('../../../client/src/config/keys');
const baseUrl = keys.darkskyBaseUrl;
const apiKey = keys.darkskyApiKey;
const lat = keys.locationLat;
const lng = keys.locationLng;
const url = `${baseUrl}${apiKey}/${lat},${lng}`;
console.log(chalk.blue('SERVER SIDE ROUTE FORECAST URL ', url));
const res = await axios.get(url);
// forecast -- strip down res, only using currently{} & daily{}
const weather = {
currently: res.data.currently,
daily: res.data.daily.data
};
console.log(chalk.yellow('SERVER SIDE ROUTE FORECAST DATA ', weather));
// return weather
res.json({ weather });
} catch (error) {
console.error(chalk.red('ERR ',error.message));
res.status(500).send('Server Error');
}
});
module.exports = router;
My Express server middleware pertaining to routes (just to be thorough)
// server/index.js
/* code deleted for brevity */
// define routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/weather', require('./routes/api/weather'));
app.use('/api/favorites', require('./routes/api/favorites'));
/* code deleted for brevity */
If the code snippets included are not sufficient, the repo resides here: https://github.com/dhawkinson/TH12-BnBConcierge
Thank you in advance for help with this.
***** Updates *****
I notice that the console logs I have in both actions/weather.js & reducers/weather.js on the client side & routes/api/weather.js on the server side are NOT firing. That tells me that those modules must not be executing. That would explain why I am getting the error "Cannot read property 'currently' of undefined" in client/src/components/pages/functional/Weather.js. Clearly I have a missing link in this chain. I just can't see what it is.
I tried a small refactor, based on input below. I was trying to see if there was some kind of naming conflict going on. this is what I did in my React functional Component:
// client/src/components/pages/functional/Weather.js
...
const mapStateToProps = state => ({weather: { forecast: state.forecast, loading: state.loading }});
...
It didn't help.
I see that in your combineReducers here you are setting as
export default combineReducers({
alert,
auth,
weather
})
So in the store, things gets saved as { alert: {...}, auth: {...}, weather: {...}}. Can you try accessing the forecast value in your Weather as state.weather.forecast ?
const mapStateToProps = state => ({ forecast: state.weather.forecast });
Let me know if it works.
You need to modify your component.
const dispatch = useDispatch();
useEffect(() => { dispatch(getWeather()); }, [getWeather])
And your mapToStateToProps should be as follows:
const mapStateToProps = state => ({ forecast: state.weather.forecast });

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
});
}
})();
});

Resources