socket.io post message twice (react) - reactjs

sometimes its post 3-4 times.
probably its about rendering. i moved init function from container.jsx to app.jsx not worked.
tried to delete cache. not worked
as you can see from console log. is chatlist rendered twice? or something like that
using socket.broadcast.emit on backend. not working
client code
import { useEffect } from "react";
import io from "socket.io-client";
let socket;
export const init = () => {
console.log("Connecting...");
socket = io.connect("http://localhost:3000", {
transports: ["websocket"],
});
socket.on("connect", () => console.log("Connected!"));
};
export const sendMessage = (message) => {
if (socket) {
socket.emit("new-message", message);
}
};
export const onNewMessage = (callback) => {
if (!socket) return;
socket.on("receive-message", (message) => {
callback(message);
console.log("Received message: ", message);
});
};
server code
const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);
const cors = require("cors");
const Messages = require("./lib/Messages");
app.use(cors());
app.get("/", (req, res) => {
res.end("Merhaba Socket.IO");
});
io.on("connection", (socket) => {
console.log("a user connected");
Messages.list((data) => {
console.log(data);
socket.emit("message-list", data);
});
socket.on("new-message", (message) => {
console.log(message);
Messages.upsert({ message });
socket.broadcast.emit("receive-message", message);
});
socket.on("disconnect", () => console.log("a user disconnected"));
});
http.listen(process.env.PORT || "3000", () => {
console.log("listening on *:3000");
});
chatlist.jsx
import { useChat } from "../context/ChatContext";
import styles from "./style.module.css";
import ChatItem from "./ChatItem";
export default function ChatList() {
const { messages } = useChat();
console.log(messages);
return (
<div className={styles.chatlist}>
<div>
{messages.map((item, key) => (
console.log("Message: ",item),
<ChatItem key={key} value={item} />
))}
</div>
</div>
);
}

its because of the useEffect bug in last version of react

Related

Getting duplicate messages with React + Socket.IO chat app

Attempting to create a simple React chat app, but I'm running into an issue where duplicate messages appear. I run the server with node server.js, in a separate terminal run the client with npm start, then open two windows at localhost:3000. I can send a message, but any message I send is received x2. For example (after submitting the form with "i only sent this once" on one window, this is what appears in the second window).:
I did console.log on the server-side, and only one message is being received from the server.
Here is my server.js file:
const express = require("express");
const socketio = require("socket.io");
const http = require("http");
const cors = require("cors");
const PORT = process.env.PORT || 5001;
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
io.on("connection", (socket) => {
socket.on("connection", () => {});
socket.on("send-message", (message) => {
console.log(message);
socket.broadcast.emit("message", message);
});
});
Here's my socket.js file:
import io from "socket.io-client";
const SERVER = "http://127.0.0.1:5001";
const connectionOptions = {
forceNew: true,
reconnectionAttempts: "Infinity",
timeout: 10000,
transports: ["websocket"],
};
let socket = io.connect(SERVER, connectionOptions);
export default socket;
Here's my App.js file:
import { useState, useEffect } from "react";
import "./App.css";
import socket from "./socket";
const ChatWindow = () => {
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const handleMessageChange = (event) => {
setMessage(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
socket.emit("send-message", { message: message });
};
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
return (
<div>
{messages.map((message) => {
return <h3>message: {message}</h3>;
})}
<form onSubmit={handleSubmit}>
<input value={message} onChange={handleMessageChange}></input>
<button type="submit">Send Message</button>
</form>
<h1>Chat Window</h1>
</div>
);
};
function App() {
return (
<div className="App">
<ChatWindow />
</div>
);
}
export default App;
As punjira answered in a comment, removing the event listener for the useEffect worked.
Specifically, before:
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
After:
useEffect(() => {
socket.on("connection", null);
socket.on("msg", (payload) => {
setMessages((prev) => {
return [...prev, payload.msg];
});
});
return function cleanup() {
socket.removeListener("msg");
};
}, []);
If you are using UseEffects Hooks you need to clean up
Before :
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
}, []);
You can add this line to clean up :
useEffect(() => {
socket.on("connection", null);
socket.on("message", (payload) => {
setMessages((prev) => {
return [...prev, payload.message];
});
});
return () => socket.off("message"); // add this line to your code
}, []);

socketio cant get new messages from server-side | react

emitting messages to server working fine but when i try to get message from server its not working.
tried different useEffect dependencies (incomingMessage etc.), after some search all sources using socket as dependency.
in server side messages is appear tring to send message to client
client
import io from "socket.io-client";
import { useEffect, useState } from "react";
const socket = io("http://localhost:3001");
function App() {
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState("");
const [incomingMessage, setIncomingMessage] = useState([]);
useEffect(() => {
socket.on("connect", () => {
setLoading(false);
});
},[]);
const sendMessage = (e) => {
e.preventDefault();
socket.emit("message", message);
setMessage("");
};
useEffect(() => {
socket.on("new-message", (data) => {
console.log("new message", data);
setIncomingMessage(data);
});
console.log("incoming message", incomingMessage);
}, [socket]);
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<h1>{socket.id}</h1>
<input value={message} onChange={(e) => setMessage(e.target.value)} />
<button onClick={sendMessage}>Send</button>
<p>{incomingMessage}</p>
</div>
);
}
export default App;
server
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
socket.on("message", (data) => {
console.log(data);
socket.emit("new-message", data);
});
});
httpServer.listen(3001);

Uncaught TypeError: _Web3Client__WEBPACK_IMPORTED_MODULE_1__.mintToken.then is not a function

I am trying to integrate my smart contract with frontend react js but I am getting this error while using methods of the contract.
I got an error of react scripts version before so I installed a version less than 5 and now I am getting this error. Is it related? Please help .
Uncaught TypeError: _Web3Client__WEBPACK_IMPORTED_MODULE_1__.mintToken.then is not a function
App.js file
import React, { useEffect, useState } from 'react';
import { init, mintToken } from './Web3Client';
function App() {
const [minted, setMinted] = useState(false);
const mint = () => {
mintToken
.then((tx) => {
console.log(tx);
setMinted(true);
})
.catch((err) => {
console.log(err);
});
};
return (
<div className="App">
{!minted ? (
<button onClick={() => mint()}>Mint Token</button>
) : (
<p>Token Minted successfully</p>
)}
</div>
);
}
export default App;
Web3Client.js File
import Web3 from 'web3';
import NFTContract from './NFT.json';
let selectedAccount;
let nftContract;
let isInitialized = false;
export const init = async () => {
let provider = window.ethereum; //
if (typeof provider !== 'undefined') {
//metamask is installed
provider
.request({ method: 'eth_requestAccounts' })
.then((accounts) => {
selectedAccount = accounts[0];
console.log(`selected account is ${selectedAccount}`);
})
.catch((err) => {
console.log(err);
return;
});
window.ethereum.on('accountsChanged', function (accounts) {
selectedAccount = accounts[0];
console.log(`Selected account changed to ${selectedAccount}`);
});
}
const web3 = Web3(provider);
const networkId = await web3.eth.net.getId();
nftContract = new web3.eth.Contract(
NFTContract.abi,
NFTContract.networks[networkId].address
);
isInitialized = true;
};
export const mintToken = async () => {
if (!isInitialized) {
await init();
}
return nftContract.methods
.mint(selectedAccount)
.send({ from: selectedAccount });
};
NFT Contract
pragma solidity >=0.8.9;
import '#openzeppelin/contracts/token/ERC721/ERC721.sol';
contract NFT is ERC721 {
constructor() ERC721('Coolest NFT', 'NFT') {}
uint256 private _tokenId = 0;
function mint() external returns (uint256) {
_tokenId++;
_mint(msg.sender, _tokenId);
return _tokenId;
}
}
.then() is a callback for promises, but your mintToken() method does not return a promise. You will need to do this instead:
export const mintToken = async () => {
return new Promise(async(resolve,reject) => {
if (!isInitialized) {
await init();
}
const _txn = nftContract.methods
.mint(selectedAccount)
.send({ from: selectedAccount });
resolve(_txn);
});
};

Why does useEffect() not updating messages object on new message?

I am new and I am following a tutorial of socket.io for real-time chatting. Users join the room and then a text box will appear. When Users type the message and send it, the messages object must store the message.
message is sending from the server but on the client side messages are not updating with the new message. Is useEffect not working? Or how can I get the array updated?
Chat.js
import queryString from 'query-string';
import io from 'socket.io-client';
let socket;
const Chat = ({ location }) => {
const [name, setName] = useState('');
const [room, setRoom] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const ENDPOINT = 'localhost:5000';
useEffect(() => {
const { name, room } = queryString.parse(location.search);
socket = io(ENDPOINT);
setName(name);
setRoom(room);
socket.emit('join', { name, room }, () => {
});
return () => {
socket.disconnect();
}
},[ENDPOINT, location.search]);
useEffect(() => {
socket.on('message', (message) => {
setMessages([...messages, message]);
});
}, [messages]);
const sendMessage = (event) => {
event.preventDefault();
if (message) {
socket.emit('sendMessage', message, () => setMessage(''));
}
console.log(message, messages);
}
return (
<div className="outerContainer">
<div className="container">
<input
value={message}
onChange={event => setMessage(event.target.value)}
onKeyPress={event => event.key === 'Enter' ? sendMessage(event) : null}
/>
</div>
</div>
);
};
export default Chat;
index.js
const socketio = require('socket.io');
const http = require('http');
const { addUser, removeUser, getUser, getUsersInRoom } = require('./users');
const PORT = process.env.PORT || 5000;
const router = require('./router');
const app = express();
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
},
});
io.on('connection', (socket) => {
socket.on('join', ({ name, room }, callback) => {
const { error, user} = addUser({ id: socket.id, name, room});
if (error) {
return callback(error);
}
socket.emit('message', { user: 'admin', text: `${user.name}, welcome to the room ${user.room}`});
socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name}, has joined`});
socket.join( user, room);
callback();
});
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message', { user: user.name, text: message});
callback();
});
socket.on('disconnect', () => {
console.log('User had left!!!');
});
});
app.use(router);
server.listen(PORT, () => console.log(`Server has started ${PORT}`));```
The problem is that you are updating the messages state in useEffect() which has messages as one of the dependencies. U can fix this problem by passing a callback to setMessages() like this:
setMessages(prevMessages => [...prevMessages, message])
and remove the messages dependency from the dependency array.

emitting action with socketio into different react components

I am trying to emit a msg into different react components and update all components with new value.
server:
const express = require('express');
const socketio = require('socket.io');
const http = require('http');
const {addPlayer, removePlayer, getPlayer, getPlayerssInRoom, addRoom} = require('./players');
const cors = require('cors');
const PORT = process.env.PORT || 5000;
const router = require('./router');
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
app.use(router);
io.on('connection', (socket) => {
socket.on('question', (question) => {
socket.emit('setQuestion', question);
socket.broadcast.to('rtd').emit('setQuestion', question);
io.sockets.in('rtd').emit('setQuestion', question);
});
socket.on('disconnect', () => {
console.log('User had left');
});
});
app.use(router);
server.listen(PORT, () => console.log(`Server has started on port ${PORT}`));
First component where I emit an action to the server:
import React, {useEffect, useState} from 'react';
import Button from 'react-bootstrap/Button';
import io from 'socket.io-client';
let socket;
const Admin = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const ENDPOINT = 'localhost:5000';
socket = io(ENDPOINT);
const publishQuestion = () => {
console.log('trying');
socket.emit('question', currentQuestion);
}
useEffect(() => {
socket = io(ENDPOINT);
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, [currentQuestion]);
return (
<div className="justify-content-center">
<h1>Admin</h1>
<input onChange={(event)=>setCurrentQuestion(event.target.value)}/>
<Button onClick={()=> publishQuestion()}>Publish word</Button>
</div>
)
}
export default Admin;
Second component where I am having issue to listen to the emit that comes from the server:
import React, {useState} from 'react';
import io from 'socket.io-client';
let socket;
const Player = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const ENDPOINT = 'localhost:5000';
socket = io(ENDPOINT);
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
return (
<h1>player</h1>
)
}
export default Player;
Anyone has an idea why the Player component not picking on the server emit (socket.on('setQuestion', (newQuestion))?
I recommend you to keep the socket client reference in one place. This way you have the same socket reference in your components and the components state updates won't affect it:
socket-client.js
import io from "socket.io-client";
const ENDPOINT = "localhost:5000";
export const socket = io(ENDPOINT);
Admin.js
import { socket } from "./socket-client";
const Admin = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
const publishQuestion = () => {
socket.emit('question', currentQuestion);
}
useEffect(() => {
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, []); //--> empty dependency
}
Player.js
import { socket } from "./socket-client";
const Player = () => {
const [currentQuestion, setCurrentQuestion] = useState('');
useEffect(()=> {
socket.on('connect', function (data) {
console.log('socket: ', socket.id);
socket.on('answer', (answer) => {
console.log('do i get here');
})
socket.on('setQuestion', (newQuestion) => {
setCurrentQuestion(newQuestion);
console.log(newQuestion);
})
});
}, [])
}
EDIT /
Use io.sockets.in if you want to emit to all socket in that room.
Use socket.broadcast.to to emit to all socket in that room except it.
server.js
io.on("connection", (socket) => {
socket.join('rtd'); //--> join to rtd room;
socket.on("question", (question) => {
// socket.broadcast.to("rtd").emit("setQuestion", question);
io.sockets.in("rtd").emit("setQuestion", question);
});
socket.on("disconnect", () => {
console.log("User had left");
});
});

Resources