Socket.io is not refreshing React component - node.js

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

Related

items array is empty when trying to add items to cart

serverso I am trying to add items to the cart but the array is empty, I cant find the problem. I think the backend which is node.js is working correctly because when I add item to cart by typing the url in the browser http://localhost:4000/cart/5dd7668f33c21d811b74f403, it is increasing the cart quantity and redirecting to http://localhost:4000/cart/ but when I navigate to http://localhost:3000/cart/ there is nothing displayed in the browser and the items array is empty when I inspect it using chrome developers tools.I think the frontend react and the backend are not linked correctly. I have added the pictures of it for further clarification and here is the code of my application.
Server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
var session = require("express-session");
var MongoStore = require("connect-mongo")(session);
var flash = require("connect-flash");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
app.use(cookieParser());
app.use(
session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true,
store: new MongoStore({ mongooseConnection: mongoose.connection }),
cookie: { maxAge: 180 * 60 * 1000 }
})
);
app.use(flash());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").get(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
console.log(req.session.cart);
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
book-details.componentjs
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: [],
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.get(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add to Cart
</button>
</div>
</div>
</div>
);
}
}
cart1.component.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cart-tem1.component.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
I have been stuck on this for almost a week now and tried all I could to make it work but to no avail, any help would be immensely appreciated
[chromedevelopertools][2]
It's hard to debug everything without trying it, but I'm pretty sure you have an issue with the session.
Sessions are implemented by cookies (things your browser sends on every request), and they depend on the domain for security reasons. They don't play well with Single Page Applications.
When you go to port :4000 the browser sends the cookies for you, but on port :3000, axios should send the cookies from that other domain. Try adding this config in your calls:
axios.get('xxx', {withCredentials: true});
It ~might work, though I feel you'll need to do some extra work somewhere else to initialize that cookie in the first place.
If you're going for the SPAs (which is popular choice nowadays) I'd recommend not using sessions and try to make your server 'Stateless'. It'll have other benefits too. Instead of that cookie thing, you'd need to implement something like Oauth or JWT (I recommend Jwt if you are getting started). That has enough keywords for you to google! :)

Cannot add to cart on frontend react.js, although the end points are working correctly and it is adding items to cart in post man app

so I am trying to add to cart. My node.js endpoints are working correctly and I am able to add items to cart when viewed in postman app but it does not display items on the front end, and when inspecting through the chrome developers tools, the items array is empty when on the postman while testing it is successfully storing items.
Here is my server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
the server response:
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
},
{
"item": "5dd6bb36725bbba1ca482eea",
"title": "Professional Node.js",
"price": 20.56,
"qty": 2
}
],
"total": 66.77
}
cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
console.log(response.data.items);
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cartitem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
here is the app.js code for cart route
<Route path="/cart" exact component={Cart}></Route>
Edited code book-details.component.js
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart/";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add To Cart
</button>
</div>
</div>
</div>
);
}
}
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart/" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
Any help would be appreciated.
i think i should point out that what you are passing to CartItem is "books" while in the CartItem component you are trying to get "items" from props (this.props.items). that part should be this.props.books.title.
UPDATE:
After you updated your question, i noticed this addition:
and on clicking the add to cart button it navigates to the link
href={"/cart"} className="button"> Add to Cart
this might be where your problem is coming from. on the API, to add books to cart, you did something like this:
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
so you are basically making a post request (even though from the code you are not really posting any data since you are just extracting the id from the url parameter. maybe you should consider making this a get request).
the key part here is the post http method and the id that is expected as the url parameter.
to make things simple on yourself, you can change your "add to cart" to something like:
<button className="button" id={book.id} onClick={this.addToCart}>
Add to Cart
</button>
for addToCart, you can do something like this:
addToCart=(e)=>{
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`)
.then(()=>{window.location.href = "http://localhost:3000/cart"})
}
note that like i said, you can replace the post request above to a get request since you are not actually posting any form data. if you wish to do this, you should also change the corresponding post request in your api to a get request.
Also, note that you can't get cart items posted through postman from the browser. you are using node sessions for cart items storage. you have to create a different react component (if you have not already created it) from where you can send the post request above to your express api

Socket.io in deeply nested Express Router

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.

Displaying API calls from nodeJS inside reactJS component

so I'm making an app where I had to create a nodeJS server and I have to display the API calls on the client side(reactJS). However, when I call the fetch() method, nothing gets displayed on the client side but only on the server side in my terminal. I probably don't know how to do it properly, so I was just wondering if any of you guys have an idea of what I'm doing wrong, I just want to learn, that's all. Here's small part of my server side:
const express = require('express');
const router = express.Router();
var key = "v2xcfdfa76db9f173028d97859c47a8ce0554321029a3fbfc06a26f81b1655bd3d9";
var BitGo = require('bitgo');
var client = new BitGo.BitGo({ env: 'test', accessToken: key });
router.get('/user/me', (req, res, next) => {
client.me({}, function callback(err, user) {
if (err) {
console.dir(err);
}
console.dir(user);
});
});
module.exports = router;
And here's the client side:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
}
}
componentDidMount() {
return fetch('/user/me')
.then((response)=> response.json())
.then((responseJson)=>{
this.setState({
data: responseJson.data
});
})
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
Data from our API: <b>{this.state.data}</b>
</p>
</div>
);
}
}
export default App;
You aren't actually returning anything from the server... or so it seems with the limited code you gave us.
router.get('/user/me', (req, res, next) => {
client.me({}, function callback(err, user) {
if (err) {
console.dir(err);
res.status(400).send(err)
}
console.dir(user);
res.send({ data: user })
});
});
I'm assuming that user is JSON serializable (and that it's the data that you want returned from the server).

react and socket io private messaging not working

server side
io.on('connection', function(socket){
socket.on('user joined', function(data){
socket.join(data.username);
});
socket.on('disconnect', function(){
console.log('user disconnected');
});
socket.on('send private', function(data){
console.log('sending private', data);
io.in(data.to).emit('pmessage',data);
})
});
client side
handleSubmit(){
var newMSG= {
to: this.state.selectedFriend,
from: this.props.user.username,
message: 'new message yay'
}
this.state.socket.emit('send private', newMSG);
}
componentDidMount(){
var self = this;
self.state.socket.on('pmessage', function(data){
console.log('receiving private!', data);
document.getElementById('myprivates').innerHTML= "<p><em>"+data.message+"</em></p>";
})
}
I can't get the 'pmessage' emit event to trigger, so I'm thinking I'm not doing the io.in() part right. I'm trying to implement private communication using the users' usernames but I can't find clear examples or documentation on this. I followed the docs on socket io so I should be close or working but it's not.
It seems like you have used the right react life cycle method but the way you are adding the data to the dom is not a good method in react. Try something like this.
import React from 'react';
import ReactDOM from 'react-dom';
const socket = io();
class MyComponent extends React.Component {
constructor() {
super();
this.state = { messages: []}
}
handleSubmit(){
var newMSG= {
to: this.state.selectedFriend,
from: this.props.user.username,
message: 'new message yay'
}
this.state.socket.emit('send private', newMSG);
}
componentDidMount(){
var self = this;
self.state.socket.on('pmessage', function(data){
console.log('receiving private!', data);
this.setState({messages:this.state.messages.concat(data)});
}
messagelist(){
return this.state.messages.map((msg,i) => {
return <li key={i}> msg </li>
});
}
render() {
let messages =
return (
<div>
<h1>Messages</h1>
<div>
{this.messagelist}
</div>
</div>
);
}
}
export default MyComponent;
The way we need to listen to new messages from socket has to be handled separately and make the component state to change with the new data.
I have added a simple example on how to do this. It may not work as it is and need some changes based on your business logic. But this should give you a fair idea on how to do it.
I didn't add a case where client talks to the server with a new message.But it's fairly simple to implement.
import React from 'react';
import ReactDOM from 'react-dom';
const socket = io();
class MyComponent extends React.Component {
constructor() {
super();
this.state = { messages: []}
socket.on('pmessage', (newMessages) => this.handleStateChange(newMessages));
}
handleStateChange(newMessages) {
this.setState({ messages: newMessages });
}
render() {
let messages = this.state.messages.map((msg) => {
return <li> msg </li>
});
return (
<div>
<h1>Messages</h1>
<div>
{messages}
</div>
</div>
);
}
}
ReactDOM.render(<MyComponent/>, document.getElementById('private-messages'));

Resources