Issue
the above img shows the proplem. you can check the console in bottom right too
I am working on one to one chat website using socket io react node,express i am facing this issue where example :when i type first hi then hi displays 1 time when i type hii it gets displayed 2 times when 3rd time i type jo it displays jo 3 times how can i fix this this is my react code Also my messaging is not receiving at other end it is displaying only on senders page
import React, { Component } from 'react';
import { Link,Redirect } from 'react-router-dom';
import UserService from "../services/userservice";
import {getUsersFriend} from "../services/messageservice";
import io from "socket.io-client";
const SOCKET_IO_URL = "http://localhost:4000/";
export default class Messages extends Component {
constructor(props){
super(props)
this.socket = io(SOCKET_IO_URL)
this.state = {
currentUser: UserService.getCurrentUser(),
isLoading:false,
userdetails:[],
show:false,
username:'',
message:'',
socketConnected:false,
messages:[]
};
this.onTextboxChangeMessage = this.onTextboxChangeMessage.bind(this)
}
componentDidMount(){
const {currentUser}=this.state
this.fetchUser()
this.socket.on('connect',()=> {
this.setState({ socketConnected : true})
// console.log("connection")
})
}
async fetchUser(){
try{
const {currentUser} = this.state
console.log(currentUser)
const data = { userid : currentUser.user._id }
console.log(data)
let user = await getUsersFriend(data)
this.setState({ userdetails: user });
// console.log(user)
}catch(err){
console.log(err)
}
}
showMessageSpace(elementusername){
this.setState({
show: true,
username:elementusername
});
}
onTextboxChangeMessage(e){
this.setState({ message:e.target.value})
}
SendMessage(e,senderid,receiverusername,message,senderusername){
e.preventDefault();
e.stopPropagation()
console.log('event', e)
const {messages} =this.state
if(this.state.socketConnected){
console.log('if condition test',senderid,receiverusername,message,senderusername )
this.socket.emit('send',{senderid,receiverusername,message,senderusername});
this.socket.on(`${receiverusername}`, (d)=>{
if(this.state[`${receiverusername}`]?.length >= 1 ){
let messageList = this.state[`${receiverusername}`]
this.setState({[`${receiverusername}`]:[...messageList,d]})
}
else{
this.setState({[`${receiverusername}`]:[d]})
console.log('else Condition store individual messages', this.state[`${receiverusername}`])
}
}
this.setState( { message:'' })
}
render(){
const { currentUser ,isLoading,userdetails,message,messages} = this.state;
// console.log(messages)
if (isLoading) {
return (<div><p>Loading...</p></div>);
}
if(!currentUser){
return(
<div>
<Redirect to='/login' />
</div>
)
}
else{
return(
<div>
<h1>Messages</h1>
<div>
<p>Users</p>
{' '}
<ul className="collection">
{userdetails.map((element) => {
return(
<div key={element._id}>
<li><Link to={`/dashboard/profile/:${element._id}`}>{element.username}</Link>{' '}<input
type="button"
id={element._id}
value="Message"
onClick={this.showMessageSpace.bind(this,element.username)} ></input></li>
</div>
);
})
}
</ul>
{' '}
</div>
{' '}
<Link to="/dashboard">Dashboard</Link>
{' '}
<div>
{
this.state.show &&
(<div>
<h2>Username : {' '}{this.state.username}</h2>
{' '}
<div>
<h3>Body</h3>
<div>
<ul>
{/* { this.state[`${this.state.username}`]?.map((msg,key) =>{ */}
{this.state.username?.length > 0 && this.state[`${this.state.username}`]?.map((msg,key) =>{
return(<li key={key}>{msg.senderusername}<span>{' '}{msg.message}</span></li>);
})
}
</ul>
</div>
</div>
{' '}
<div>
{' '}
<input
type="text"
name="message"
value={message}
onChange={this.onTextboxChangeMessage}
></input>
<button className='btn btn-info' onClick={(e)=> {this.SendMessage(e,currentUser.user._id,this.state.username,this.state.message,currentUser.user.username)}}>Send</button>
</div>
{' '}
</div>)
}
</div>
</div>
)
}
}
}
server code:
io.on('connection', (socket) => { /* socket object may be used to send specific messages to the new connected client */
console.log('connection established',socket.id);
socket.on('send', (data)=>{
console.log("Receive data from single username",data)
io.emit('message',data)
socket.on('message',data => {
console.log("private")
io.to(data.receiverid).emit('message',data)
})
});
socket.on('disconnected',()=>{
console.log("disconnect")
})
});
Solution
This is because you assign another new callback to the socket every time you send a message.
Move this part:
this.socket.on(`${receiverusername}`, (d)=>{
if(this.state[`${receiverusername}`]?.length >= 1 ){
let messageList = this.state[`${receiverusername}`]
this.setState({[`${receiverusername}`]:[...messageList,d]})
} else {
this.setState({[`${receiverusername}`]:[d]})
console.log('else Condition store individual messages',
this.state[`${receiverusername}`])
}
into onconnect callback:
this.socket.on('connect',()=> {
this.setState({ socketConnected : true})
this.socket.on("message", (d)=>{
if(this.state[`${d.receiverusername}`]?.length >= 1 ){
let messageList = this.state[`${d.receiverusername}`]
this.setState({[`${receiverusername}`]:[...messageList,d.content]})
} else {
this.setState({[`${d.receiverusername}`]:[d.content]})
console.log('else Condition store individual messages',
this.state[`${receiverusername}`])
}
})
But in this variant, you don’t have receiverusername.
So you should send general "message" event from your server as object containing receiverusername.
The on method is exactly explained in socket.io documentation: https://socket.io/docs/v3/listening-to-events/
And pattern with assigning onmessage callback in the onconnect handler is documented here: