[Fixed] Whenever i send msg it is showing same message multiple times in react

Issue

enter image description here
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:

https://socket.io/get-started/chat/#Broadcasting

Leave a Reply

(*) Required, Your email will not be published