psquizzle
psquizzle

Reputation: 185

Using Sockets.io with React.js + Hooks; sockets.emit Not Working

I am at a complete loss as to why this is not working, I am new to using Hooks in react however want to try.

This app basically connects sockets.io to the server using sockets-auth server emits the time every 1s <- this shows me that the connection is definitely live. It then uses sockets.emit to receive an array of data, this functions. The part that doesn't is a simple button press fails to achieve anything (see both 'Save Changes' buttons at bottom of React element). No error, no response, no acknowledgment at all. I have tried it inline, in its own function. The exact same server routes are functional simultaneously running a separate app with class components.

Help would be much appreciated. Anyway here is some code..

Client-Side (React.js)

import React, { Component,useState, useEffect } from "react";
import io from "socket.io-client";
import './Secret.css'

const Secret = () => {
  const [time, setTime] = useState('');
  const [userlist, setUserlist] = useState([]);



  const socketUrl = 'http://localhost:3001';
  let socket = io(socketUrl, {
    autoConnect: false,
  });


  useEffect(() => {
    const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'React POST Request Example' })
  };

  fetch('/api/connectsocket', requestOptions)
      .then(async response => {
          const data = await response.json();

          if (!response.ok) {

              const error = (data && data.message) || response.status;
              return Promise.reject(error);
          }


          console.log(data)
          connectSocket(data)
      })
      .catch(error => {

          console.error('There was an error!', error);
      });    

  }, [socketUrl]);



  const connectSocket= (data)=>{
    let dataUser = data
    console.log(dataUser.email)
    let error = null;



  socket.on('connect', () => {
    console.log('Connected');


    socket.emit('authentication', {
      token: dataUser.email,
      i: dataUser.i
    });


  });

  socket.on('unauthorized', (reason) => {
    console.log('Unauthorized:', reason);

    error = reason.message;

    socket.disconnect();
  });

  socket.on('disconnect', (reason) => {
    console.log(`Disconnected: ${error || reason}`);


    error = null;
  });




  socket.on("admin connected", data => {
    socket.emit('admin userlist', { get:'x',for:'who' }, (error) => {
      if(error) {
        alert(error);
      }
    });
     console.log(data)
   });
   socket.on("admin user list", data => {
   setUserlist(data)
     console.log(data)
   });


  socket.on("FromAPI", data => {
   setTime(data)
    console.log(data)
  });


  socket.open();

  }

const sendMessage = (x)=>{

console.log(x)
  socket.emit('hello', 'JSON.stringify(x)');

}

const doSomething = ()=>{


  socket.emit('chatmessage', {msg:"Hello"});

  }


  return (
    <div>
    <div className="outerContainer">
      <div className="container">
         {time}
         <button onClick={sendMessage}>Save Changes</button>
         <button onClick={doSomething}>Save Changes</button>




          {userlist.map(user => (
          <tr key={user.id} value={user.id}>
            <td>{user.email}</td>
            <td>{user.email}</td>
            <td>{user.email}</td>
          </tr>
            ))}




      </div>

    </div>
    </div>
  );

}
export default Secret

Server-side (Node.js)

const http = require('http');
const cors = require('cors');
const express = require('express');

const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const io = require('socket.io')();
const socketAuth = require('socketio-auth');


//custom modules
const router = require('./modules/router');
const db = require('./modules/db.js');
const prettyDate = require('./modules/prettyDate.js');
const sortArray = require('./modules/sortArray.js');

const app = express();
const server = http.createServer(app);

io.attach(server);

app.use(cors()); ///delete for production
app.use(bodyParser.urlencoded({ extended: true }));
app.use(fileUpload({
    createParentPath: true
}));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(router);
app.use(express.static('public'))

db.i12345.sessions.persistence.setAutocompactionInterval(400)

 ////Sessions Store; not important if you want to run the code without
function setUser(id,user,i){
console.log('setuser')
let institution = i
  db[institution].sessions.find({ user:user }, function (err, docs) {
 if(docs.length){
  db[institution].sessions.update({user:user  }, { $set: { id: id } }, { multi: true }, function (err, numReplaced) {

  });


 } else {
  var d = new Date();
  var n = d.getTime();
  var doc = { user: user
               , id: id
               , t:n

               };

    db[institution].sessions.insert(doc, function (err, newDoc) {  

      });
 }



});
}




///user verification; could easily mock; if wanting to actually run code
async function verifyUser (token,i) {
    var institution =i
  return new Promise((resolve, reject) => {
    // setTimeout to mock a cache or database call
    setTimeout(() => {

      var user = new Promise((resolve,reject) =>{
        db[institution].users.find({email:token}, function (err, docs) {
            if(docs.length){

                resolve(docs);
             } else {
             reject(false)
             }

              });
    });


      if (!user) {
        return reject('USER_NOT_FOUND');
      }

      return resolve(user);
    }, 200);
  });
}



//// Sockets auth implementation
socketAuth(io, {

  authenticate: async (socket, data, callback) => {
    const { token, i } = data;
    console.log(data)

    try {
      const user = await verifyUser(token, i);




      socket.user = user;
      socket.i = i
      console.log(i)

      return callback(null, true);
    } catch (e) {
      console.log(`Socket ${socket.id} unauthorized.`);
      return callback({ message: 'UNAUTHORIZED' });
    }
  },
  postAuthenticate: async (socket) => {
    console.log(`Socket ${socket.id} authenticated.`);
    console.log(socket.user)
    setUser(socket.id,socket.user, socket.i)

    socket.emit('empty list', {queryList:true});

    io.emit('admin connected', {admin:true});

    socket.conn.on('packet', async (packet) => {
      if (socket.auth && packet.type === 'ping') {
      }
    });
    socket.on('chatmessage', (msg) => {
      io.emit('chatmessage', msg);
      console.log(msg);
    });
    socket.on('hello', (msg) => {
        io.emit('chatmessage', msg);
        console.log(msg);
      });

    socket.on('get tasks', (get) => {
     let i = socket.i
     let user = socket.user
     getTasksChunk(get,i,user)

      });

      socket.on('admin userlist', (get) => {
        let i = socket.i
        let user = socket.user

        adminGetUserList(get,i,user)


         });

         socket.on('admin roles', (data) => {
            let i = socket.i
            let user = socket.user
                console.log(data)
                console.log('reaced')



             });




    interval = setInterval(() => getApiAndEmit(socket), 1000);

  },



  disconnect: async (socket) => {
    console.log(`Socket ${socket.id} disconnected.`);

    if (socket.user) {

    }
  },
})

function getTasksChunk(get,i, user){
    console.log(get,i,user);
    let institution = i

        db[institution].tasks24.find({}, async function (err, docs) {
            if(docs.length){
                for(i=0;i<docs.length;i++){
                    docs[i].location = `Ward: ${docs[i].location.Ward} Bed: ${docs[i].location.Bed}`
                    docs[i].patient = docs[i].patient.join(' | ')
                    docs[i].timestamp = prettyDate(new Date(+docs[i].timestamp))
                  } 
                  console.log(docs)
                let sorted = await sortArray(docs,'timestamp')
                let chunk = sorted.slice(0,10)

               io.emit('tasks in', chunk);
             } else {

             }

              });





}

const getApiAndEmit = socket => {
    const response = new Date();
    // Emitting a new message. Will be consumed by the client
    socket.emit("FromAPI", response);
  };


  ///////ADMIN????????

 function adminGetUserList(get,i,user){
     var institution = i
     console.log('hello')
    db[institution].users.find({}, async function (err, docs) {
        if(docs.length){

              console.log(docs)
            let sorted = await sortArray(docs,'timestamp')


           io.emit('admin user list', sorted);
         } else {

         }

          });
  }
server.listen(process.env.PORT || 3001, () => console.log(`Server has started.`));

Upvotes: 2

Views: 7407

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 281656

You must store your socket instance in a ref otherwise you would loose the connected socket instance when your component re-renders.

In short you need the socket reference to be the same across renders

  const socketUrl = 'http://localhost:3001';
  let socket = useRef(null);

  useEffect(() => {
      socket.current = io(socketUrl, {
           autoConnect: false,
      });
      ...
  }, [socketUrl]);

Now note that whereever you are using socket you would use socket.current.

Ex:

 socket,.current.on('hello', (msg) => {
    io.emit('chatmessage', msg);
    console.log(msg);
  });

Upvotes: 10

Related Questions