Socket.io in deeply nested Express Router - node.js

I am trying to pass a socket.io instance into Express Routes. Currently I am setting up the socket.io server as shown on their documentation. To pass the object into the routes I am using app.set('io', io) and then in the route const io = req.app.get('io') within the route. After getting the io object, I am simply emitting to all listening users. This works for the first request but then the second time the same request is made, two events get sent to every client. The third request emits the data three times and so on.
// App.js file
// Server imports
const app = require('express')(),
http = require('http').Server(app),
io = require('socket.io')(http),
session = require('express-session');
// Add socket.io to req for use in latter requests
app.set('io', io);
io.on('connection', (socket) => {
console.log('User has connected to socket.io');
socket.on('disconnect', () => {
console.log('User has disconnected');
});
});
// Route Setups
app.use('/api', require('./routes'));
http.listen(process.env.PORT, () => {
console.log(`Server running on port: ${process.env.PORT}`);
});
// Route index.js
const router = require('express').Router();
router.use('/project', require('./project/project'));
module.exports = router;
// Project Routes
const router = require('express').Router(),
Project = require('../../models/project');
// Route for creating new Projects
router.post('/', (req, res, next) => {
Project.createProject(req.body.item, (err, doc) => {
if(err) return next(err);
res.json({_id: doc._id, projectName: doc.projectName});
const io = req.app.get('io');
io.emit('project', {project: {
_id: doc._id,
client: doc.client,
jobId: doc.jobId,
projectName: doc.projectName,
eventDate: doc.eventDate,
dateModified: doc.dateModified,
eventLogo: doc.eventLogo
}});
})
});
// index.js
// Import React Standard components
// Import and setup BrowserRouter
import App from './components/App';
import io from 'socket.io-client';
const socket = io('http://192.168.1.2:8080');
ReactDOM.render(
<BrowserRouter>
<App socket={socket}/>
</BrowserRouter>
, document.getElementById('root'));
// Front End React File for displaying projects - App.js
// I cut down on some of the code to keep it simple.
import React, { Component } from 'react';
import NewProject from './NewProject';
export default class Projects extends Component {
state = {
projects: []
newProject: false
}
closeWindow = () => {
// Sets the state for new Project to true or false to show or hide the component
}
componentDidMount = () => {
// Fires mutiple times for each request made to update project or create new project
this.props.socket.on('project', ({project, index}) => {
console.log("DATA RECIEVED", project, index);
const projects = this.state.projects;
// If a project and index is sent, update an excising project
if(project !== undefined && index !== undefined) {
console.log('Project to ypdate: ',projects[index])
projects[index] = Object.keys(projects[index]).reduce((acc, key) => {
// Update all keys passed down from the server
}, {});
// If just a project is sent, add a new project
} else if(project !== undefined && index === undefined) {
projects.push(project);
// If just an index is sent, remove the project
} else if(index !== undefined) {
const projectIndex = this.props.projects.findIndex((item) => {
if(item._id === index) return true;
return null;
});
projects.splice(projectIndex, 1);
}
return this.setState({projects});
});
}
render() {
return [
this.state.newProject && <NewProject key="0" closeWindow={this.closeWindow} />
<main key="1">
// Display the projects
<button type="button" onClick={this.closeWindow}>New Project</button>
</main>
]
}
// Front End React File for Creating New Project
// NewProject.js
import React, { Component } from 'react';
export default class CreateProject extends Component {
state = {
client: '',
projectName: '',
jobId: '',
eventDate: '',
eventLogo: '',
completeForm: false
}
createProject = e => {
if(!this.state.completeForm) return;
fetch('/api/asset/project', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(this.state)
})
.then(res => res.json())
.then(res => {
return this.props.closeWindow();
})
.catch(error => {// Handle Error})
}
render() {
return (
<div className="popup">
<form onSubmit={this.createProject}>
// All inputs for form.
</form>
</div>
)
}
}
I was expecting each user to revive the data emitted once but depending on how many times the request is made, they get the data that many times.
Thank you so much!!

Please check how many times the app.js file is called. If it is called multiple times there are multiple events of io.on("connection"..
Hence the user receives data multiple times.

What worked for me was adding this to the Component Will Unmount
componentWillUnmount = () => {
this.props.socket.off('project');
}
I did not realize the event listener was not detaching itself when the component unmounted.

Related

Fetch only shows when re-rendering or refreshing page

I have a quiz game project where I've done both backend and frontend. In FE I'm fetching clues for the quiz from BE, and everything works except one thing: When playing the quiz for the first time in a while, the clue data that should be fetched doesn't show. Like this:
But when re-rendering the component or refreshing the whole browser it shows perfectly. Like this:
It also shows perfectly while working on the project, so it's only that first time when you haven't been refreshing or re-rendering in a while that it doesn't show. Is there anything to do to make it work smoothly every time? And could the problem be in FE or BE?
This is the component where I'm fetching:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { game } from 'reducers/game';
import swal from 'sweetalert';
import { Mapillary } from 'components/Mapillary/Mapillary';
import { Loading } from 'components/Loading/Loading';
import { OuterWrapper, InnerWrapper } from 'GlobalStyles';
import { ClueWrapper, MapillaryContainer, ClueContainer, SpecialSpan, ClueText, AnotherClueButton } from './Clues.Styles'
export const Clues = () => {
const [games, setGames] = useState([])
const [loading, setLoading] = useState(false);
const [currentClue, setCurrentClue] = useState(0);
const [level, setLevel] = useState(5);
//* Fetching clues
const fetchClues = () => {
console.log('loading');
setLoading(true);
fetch('https://final-project-api-veooltntuq-lz.a.run.app/games')
.then((response) => {
return response.json()
})
.then((response) => {
setGames(response.games)
console.log('data is fetched')
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}
//* Setting current score
const currentScore = useSelector((store) => store.game.score);
const dispatch = useDispatch();
const handleClick = () => {
setCurrentClue(currentClue + 1);
if (currentClue < 4) {
dispatch(game.actions.setScore(currentScore - 1)); //* If clue index < 4 = Set score to -1
setLevel(level - 1);
} else {
dispatch(game.actions.setScore(currentScore === 1)); //* If clue index > 4 = Set score to 1
swal('Time to make a guess!', {
button: 'OK'
});
}
};
useEffect(() => {
fetchClues()
}, [])
const activeClue = games[currentClue];
if (loading) {
return (
<OuterWrapper>
<InnerWrapper>
<Loading />
</InnerWrapper>
</OuterWrapper>)
}
if (currentClue < 5) { //* Stop showing clues after clue 5
return (
<div>
<MapillaryContainer>
{console.log(currentClue)}
<Mapillary width="auto" height="94vh" imageId={currentClue === 0 ? '343242160559702' : currentClue === 1 ? '463849228173207' : currentClue === 2 ? '273852291114652' : currentClue === 3 ? '953489715410448' : currentClue === 4 ? '814918985897976' : ''} />
</MapillaryContainer>
<ClueWrapper>
<ClueContainer>
<SpecialSpan>{level} points</SpecialSpan>
<ClueText>{activeClue && activeClue.gameOne}</ClueText>
<AnotherClueButton type="button" onClick={() => handleClick()}>I need another clue</AnotherClueButton>
</ClueContainer>
</ClueWrapper>
</div>
)
} else {
return (
<div>
<MapillaryContainer>
<Mapillary width="auto" height="94vh" imageId="814918985897976" />
</MapillaryContainer>
</div>
)
}
}
And this is the BE:
import express from "express";
import cors from 'cors'
import mongoose from 'mongoose'
import games from "./data/games.json";
const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/games"
mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true })
mongoose.Promise = Promise
// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
const Game = mongoose.model("Game", {
id: Number,
gameOne: String,
gameTwo: String
});
if(process.env.RESET_DB) {
const resetDataBase = async () => {
await Game.deleteMany();
games.forEach(singleGame => {
const newGame = new Game(singleGame);
newGame.save();
})
}
resetDataBase();
}
const port = process.env.PORT || 8080;
const app = express();
// Add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());
// Start defining your routes here
app.get("/", (req, res) => {
res.send({
Welcome:
"This is the API for my final project: A quiz game called StreetSmart.",
Routes: [
{
"/games": "Show all games."
}
],
})
});
// Show all games
app.get("/games", async (req, res) => {
const allGames = await Game.find().sort({"id":1})
res.status(200).json({
success: true,
games: allGames
});
});
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});

State is null when trying to access it in the parent component, but able to pass it down to child component

EDIT: Simplified the code a bit by using two separate states. Adding the channel state to the dependency array of useEffect() does not remove the error but does make the application run in a strange manner: If I try to send messages, I see multiple messages.
EDIT 2: If I use the useRef() hook to store the state, my application works albeit in a strange manner again: the state is one step slower than the actual user input.
I'm trying to build a simple chat application using React.js and Socket.IO where a single user can communicate with multiple users. I have an endpoint from where I fetch all my channel data and store it in a state but I'm not able to access the state (returns null) even if the state is being passed down to a different component as props and that is being rendered correctly the first time. When I click on a channel to select it, the channel list is again reset to null and I get this error:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'forEach')
I'm trying to implement this by following the tutorial from here. It's outdated, so I'm implementing a newer version of it. But even then, if I use the component class defined in the author's github, it seems to work just fine. I have not worked with component classes before, so I am not sure where I am going wrong.
Main component: Chat.js
import React, { useEffect, useState } from 'react';
import ChannelList from './ChannelList';
import './chat.css';
import MessagePanel from './MessagePanel';
import { io } from "socket.io-client";
const SERVER = "http://localhost:8080";
let socket;
function Chat() {
const [channel, setChannel] = useState(null)
const [channels, setChannels] = useState(null) //the state where the channels are loaded
const channelsRef = useRef() //edit 2
useEffect(() => {
loadChannels() //the function which loads the channels into the state
configureSocket()
}, []) //adding channel to the dependency array results in strange behavior.
const configureSocket = () => {
socket = io(SERVER)
socket.on('connection', () => {
if(channel) {
handleChannelSelect(channel.id)
}
});
socket.on('channel', channel => {
console.log('channel details', channel); //returns the correct channel clicked on
//let temp_channels = channels; //edit 2
let temp_channels = channelsRef.current; //edit 2
console.log('channels inside onchannel', temp_channels); //returns null, does not return null if useRef() is used
temp_channels.forEach(c => { //I get the above mentioned error at this line, after edit 2 I don't get any error
if(c.id === channel.id) {
c.participants = channel.participants
}
})
//setChannels(channels); edit 2
channelsRef.current = temp_channels // edit 2
})
socket.on('message', message => {
// let temp_channels = channels
let temp_channels = channelsRef.current //edit 2
temp_channels.forEach(c => {
if (c.id === message.channel_id) {
if (!c.messages) {
c.messages = [message];
} else {
c.messages.push(message);
}
}
})
// setChannels(channels) edit 2
channelsRef.current = temp_channels // edit 2
})
}
const handleSendMessage = (channel_id, text) => {
socket.emit('send-message', { channel_id, text, senderName: socket.id, id: Date.now()})
}
const handleChannelSelect = (id) => {
const channel = channels.find(c => {
return c.id === id;
});
setChannel(channel);
socket.emit('channel-join', id, ack => {});
}
const loadChannels = async () => {
await fetch('http://localhost:8080/getChannels')
.then(async response => {
const data = await response.json()
console.log('data inside loadchannels', data.channels);
setChannels(data.channels); //this sets the channels and passes it down to the child component "ChannelList"
channelsRef.current = data.channels //if setChannels is not used before this, it doesn't work
})
console.log('after fetching channels inside loadchannels', channel, channels); //both channel and channels state are null
}
return (
<div className="chat-app">
<ChannelList channels={channels} onSelectChannel={handleChannelSelect}></ChannelList>
<MessagePanel onSendMessage={handleSendMessage} channel={channel}/>
</div>
);
}
export default Chat;
ChannelList.js
import React from 'react';
import Channel from './Channel';
function ChannelList(props) {
const handleClick = (id) => {
props.onSelectChannel(id);
}
console.log('inside channellist component', props.channels); //returns the set of channels received from props
let list = <div className="no-content-message">There are no channels to show</div>;
if (props.channels && props.channels.map) {
list = props.channels.map(c =>
<Channel
key={c.id}
id={c.id}
name={c.name}
participants={c.participants}
onClick={handleClick}
/>
);
}
return (
<div className='channel-list'>
{list}
</div>
);
}
export default ChannelList;
Backend index.js:
const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io');
app.use(cors());
const server = http.createServer(app);
const STATIC_CHANNELS = [
{
name: 'Global chat',
participants: 0,
id: 1,
sockets: []
},
{
name: 'Funny',
participants: 0,
id: 2,
sockets: []
},
{
name: 'Test',
participants: 0,
id: 3,
sockets: []
},
];
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
});
server.listen(8080, () =>
console.log('Server is running on port 8080')
);
io.on('connection', (socket) => {
console.log(`User connected ${socket.id}`);
socket.emit('connected', socket.id);
socket.on('channel-join', id => {
console.log('channel join', id);
STATIC_CHANNELS.forEach(c => {
if (c.id === id) {
if (c.sockets.indexOf(socket.id) == (-1)) {
c.sockets.push(socket.id);
c.participants++;
io.emit('channel', c);
}
} else {
let index = c.sockets.indexOf(socket.id);
if (index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
}
});
return id;
})
socket.on('send-message', message => {
io.emit('message', message);
})
socket.on('disconnect', () => {
STATIC_CHANNELS.forEach(c => {
let index = c.sockets.indexOf(socket.id);
if(index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
})
})
});
app.get('/getChannels', (req, res) => {
res.json({
channels: STATIC_CHANNELS
})
})

How to use socketio inside controllers?

I have a application created with Reactjs,Redux,Nodejs,MongoDB. I have created socketio in backend side.
server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const routes = require('./routes/api/routes');
var server = require('http').Server(app);
const io = require('./socket').init(server);
app.use(express.json());
require('dotenv').config();
mongoose.connect(process.env.MONGO_DB).then(console.log('connected'));
const corsOptions = {
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus: 200,
};
app.use(cors(corsOptions));
app.use(routes);
if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging'
) {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
io.on('connection', (socket) => {
console.log('New client connected');
socket.on('disconnect', () => {
console.log('Client disconnected');
});
});
server.listen(process.env.PORT || 5000, () => {
console.log('socket.io server started on port 5000');
});
socket.js
let io;
module.exports = {
init: (httpServer) => {
return (io = require('socket.io')(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
}));
},
getIO: () => {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io;
},
};
In controller.js I am creating new item to inside mongodb and also using socketio. I am emitting new Item that I created. It looks like that
controller.js
const createTeam = async (req, res) => {
const userId = req.user.id;
const newItem = new Item({
name: req.body.name,
owner: userId,
});
await newItem.save((err, data) => {
if (err) {
console.log(err);
return res.status(500).json({
message: 'Server Error',
});
}
});
await newItem.populate({
path: 'owner',
model: User,
select: 'name',
});
await User.findByIdAndUpdate(
userId,
{ $push: { teams: newItem } },
{ new: true }
);
io.getIO().emit('team', {
action: 'creating',
team: newItem,
});
res.json(newItem);
};
In frontend side I am listening the socketio server with socket.io-client. In my App.js I can see data that come from backend side when I console.log(data). My app working perfect until this stage. I can take the data from socketio, I can see the new client that connected. But when I send the data with dispatch, I app start to add infinite new items to database. Take a look at my App.js
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { AppNavbar } from './components/AppNavbar';
import { TeamList } from './components/TeamList';
import { loadUser } from './Store/Actions/AuthActions';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { io } from 'socket.io-client';
import { addItems } from './Store/Actions/itemActions';
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUser());
const socket = io('http://localhost:5000');
socket.on('team', (data) => {
if (data.action === 'creating') {
dispatch(addItems(data.team));
}
// console.log(data);
});
}, []);
return (
<div className="App">
<AppNavbar />
<TeamList />
</div>
);
}
export default App;
My itemAction in redux side is also like that
export const addItems = (input) => (dispatch, getState) => {
axios
.post('/api/items/createTeam', input, tokenConfig(getState))
.then((res) =>
dispatch({
type: ADD_ITEM,
payload: res.data,
})
)
.catch((err) =>
dispatch(
returnErrors(err.response.data, err.response.status, 'GET_ERRORS')
)
);
};
My problem is how to stop infinite callling of api after implement socketio. How to stop infinite loop efficiently?
Infinite loop is steming from not dispatching "type: ADD_ITEM" once.This issue cause that your itemAction always would dispatch "type: ADD_ITEM" and payload with it when after every fetching then your react would re-render page again and again.
You should get rid of your dispatching action inside of addItems function and dispatch your action only inside of useEffect in App.js file .
Your code snippet should look like this in App.js:
useEffect(() => {
//...
const socket = openSocket('http://localhost:5000');
socket.on('postsChannel', (data) => {
if (data.action === 'creating') {
dispatch({ type: ADD_ITEM, payload: data.team });
}
});
}, [dispatch]);

React.js useState() is not getting any values;

I am learning React.js and got problem in a place.
I am fetching data from localhost:4000/products (i.e)
Here is my localhost:4000/products page
And I am doing the backend query in node.js
Here is the page:
const express = require('express');
const app = express();
const cors = require("cors");
const mysql = require("mysql");
app.use(cors());
const selectallquery = "SELECT * FROM users";
const db = mysql.createPool({
host: "localhost",
user: "root",
password: "Sakthi#1234",
database: "reactSql",
});
app.get("/",(req,res)=> {
res.send('Hello Sakthi')
});
app.get("/products/add", (req,res)=> {
const { name } = req.query;
console.log(name);
const insertquery = `INSERT INTO users (name) VALUES ('${name}')`;
db.query(insertquery, (err,result)=> {
if(err){
return res.send(err)
}
else{
return res.send("Successfully added...`")
}
});
});
app.get("/products", (req,res)=> {
db.query(selectallquery, (err,result)=> {
if (err){
return res.send(err)
}
else{
return res.send(result)
}
});
});
app.listen(4000,()=> {
console.log("Running on server 4000")
});
And here is my React.js page:
import React, { useState, useEffect } from 'react'
import axios from "axios";
function Login() {
const [names, setnames] = useState([])
useEffect(()=> {
axios.get("http://localhost:4000/products")
.then(res => {
if (res !== ""){
setnames([...names,res.data.name])
}
})
.catch(err => {
console.log(err)
})
},[])
return (
<div>
{
names
}
</div>
)
}
export default Login
The state names is not set with any value.
And it is not showing any values:
Its the page showing nothing :(
And Also i want to know how to make the useEffect hook not to render on first render(componentWillMount) and only to render at last(componentDidMount).
This is my console of res:
First, you need to update setnames because res.data is an array so you can not use res.data.name:
setnames(res.data)
And in return, you need to use map to show name:
{names.map((item) => (
<p key={item.id}>{item.name}</p>
))}
res.data is an array, so if you want to only keep names, you need to map on it to only store names to your state
setnames(names => [...names, ...res.data.map(({name}) => name)])
You can use useRef to set the node not to render at the first time.
Make React useEffect hook not run on initial render

Socket.io is not refreshing React component

I have a Server and Client with sockets, the connection is working between them, but my React component is not refreshing.
My component is a publication that is printed by an array of elements, and I get all this information from and API (Express) connected to a MySQL database.
This is my React file:
import React, { Component } from 'react';
import './component.css';
import io from 'socket.io-client';
import Msg from './components/Msg';
import ItemMsg from './components/ItemMsg';
import Sidebar from './components/Sidebar';
let socket = io('http://localhost:8082');
class App extends Component {
constructor(props){
super(props);
this.state = {
ids: []
};
}
componentDidMount() {
socket.on('update:component', (data) => {
// Debug line
console.log('recived message:', data);
// Update state
this.setState(prevState => {
// Get previous state
const { ids } = prevState;
// Add new item to array
ids.push(data);
// Debug line
console.log('new state:', ids);
// Return new state
return { ids };
});
});
fetch('/todos/ids')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ids: responseJson.data})
console.log(responseJson.data);
})
}
render() {
return (
<div>
<Sidebar/>
<Msg/>
<ul>
{
this.state.ids.map((i, k) =>
<li key={k}><ItemMsg idfromparent={i.ID_Publicacion}/></li>
)
}
</ul>
</div>
);
}
}
export default App;
This is my Server file:
var express = require('express');
var http = require('http');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var messages = [{
id: 1,
text: "I'm a message",
author: "I'm the author"
}];
app.use(express.static('public'));
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept-Type');
res.header('Access-Control-Allow-Credentials', 'true');
next();
})
app.get('/hello', function(req, res) {
res.status(200).send("Hello World!");
});
io.on('connection', function(socket) {
console.log('Alguien se ha conectado con Sockets');
socket.on('update:component', function(data) {
socket.emit('Thanks', data);
console.log(data);
});
});
server.listen(8082, function() {
console.log("Server corriendo en http://localhost:8082");
});
Insert component:
import React, { Component } from 'react';
import avatar from '../avatar.jpg'
import '../component.css'
import io from 'socket.io-client';
let socket = io('http://localhost:8082');
class Msg extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
handleSubmit(e){
e.preventDefault();
var self = this;
// On submit of the form, send a POST request with the data to the server.
fetch('/todo/meterla',{
method: 'POST',
body:JSON.stringify({
publicacion: self.refs.task.value
}),
headers: {"Content-Type": "application/json"}
})
.then(function(response){
return response.json()
}).then(function(data){
console.log('json:', data);
socket.emit('update:component', data );
});
}
componentDidMount(){
socket.on('update:component', (data) => {
// Update state
this.setState(prevState => {
// Get previous state
const { ids } = prevState;
// Add new item to array
ids.push(data);
// Return new state
return { ids };
});
});
}
handleChange(event) {
this.setState({value: event.target.value});
}
render(data) {
return (
<div>
<form className="post" action="index.html" method="post" onSubmit={this.handleSubmit.bind(this)}>
<img className="avatar" src={avatar} alt="" />
<input type="text" placeholder="Escribe algo" ref="task" value={this.state.value} onChange={this.handleChange}/>
<input type="submit" value="Publicar" />
</form>
</div>
);
}
}
export default Msg;
this is the result of the logs
Issue
I want to automatically refresh the component when I insert a data in the database, and thats why i've implemnted the websockets. The fetch brings me a select from the database
I guess you mean every time you send a post request trough handleSubmit(), right?
Post request
Handle your post request with express:
You need to emit a custom event every time you send the post request:
Emits an event to the socket identified by the string name. Any other parameters can be included.
app.post("/foo", function(req, res, next) {
io.sockets.emit("foo", req.body);
res.send({});
});
See: docs
Get value from input
In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState().
See: Controlled components
// Set state
this.state = {value: ''};
...
handleChange(event) {
this.setState({value: event.target.value});
}
...
// Render
<input type="text" value={this.state.value} onChange={this.handleChange} />
Handling Events
This binding is necessary to make this work in the callback
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
See: docs
Setup react component + socket.io
const io = require('socket.io-client');
...
class MyComponent extends React.Component {
ComponentWillMount(){
this.socket = io();
}
}
See: Set up React component to listen to socket.io
Listen for event
Register a new handler for the given event.
See: docs
You are only updating the state only one time on fetch(), trigger updateState inside a listener:
ComponentDidMount(){
// Listen for custom event and add handler
this.socket.on('update:component', (data) => {
...
Using state correctly
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
Do Not Modify State Directly, the only place where you can assign this.state is the constructor:
this.setState(prevState => {
// Get previous state
const { ids } = prevState;
// Add new item to array
ids.push(data);
// Return new state
return { ids };
});
}
Render
JSX allows embedding any expressions in curly braces so we could inline the map() result
{
this.state.ids.map((i, k) =>
<li key={k}><ItemMsg idfromparent={i.ID_Publicacion}/></li>
);
}
Github
websockets_react/issues/1
Resources
Socket.io: documentation
React-state: documentation
Npm-package: react-socket-io
Learn: React + socket.io

Resources