Notifications with Socket.io - how to emit to a specific user/recipient?

Viewed 2367

I want to implement a simple notification system. When user1 likes user2's post, user2 should get a real-time notification from user1.

Here is a part of the client function (Redux action) where someone likes a post:

.then(() => {
  const socket = require("socket.io-client")(
    "http://localhost:5000"
  );
  socket.emit("like", user, post);
});

Here is the server socket function where a notification is created after user1 likes user2's post:

io.on("connection", (socket) => {
    socket.on("like", async (user, post) => {
        if (!post.post.likedBy.includes(user.user._id)) {
            const Notification = require("./models/Notification");

            let newNotification = new Notification({
                notification: `${user.user.username} liked your post!`,
                sender: user.user._id,
                recipient: post.post.by._id,
                read: false,
            });

            await newNotification.save();

            io.emit("notification");
        }
    });
});

Here is the client function after the notification is created:

socket.on("notification", () => {
        console.log("liked");
});

Now the problem with this is the console.log('liked') appears for both user1 and user2. How can I emit to only that user that receives the notification? How can socket.io find this specific user2 that receives the notification from user1?

1 Answers

You should store a list (array or object) of all users like this :

(note that the list has to be updated when a user connects or leaves the socket server)

// an example of structure in order to store the users
const users = [
  {
    id: 1,
    socket: socket
  },
  // ...
];

And then you can target the post owner and send him a notification like this :

// assuming the the 'post' object contains the id of the owner
const user = users.find(user => user.id == post.user.id);
// (or depending of the storage structure) 
// const user = users[post.user.id]
user.socket.emit('notification');

Here an example :

const withObject = {};
const withArray = [];

io.on('connection', socket => {
  const user = { socket : socket };
  socket.on('data', id => {
    // here you do as you want, if you want to store just their socket or another data, in this example I store their id and socket
    user.id = id;
    withObject[id] = user;
    withArray[id] = user;
    // or withArray.push(user);
  });

  socket.on('disconnect', () => {
    delete withObject[user.id];
    delete withArray[user.id];
    // or let index = users.indexOf(user);
    // if(index !=== -1) users.splice(index, 1);

  });
});

There is plenty way of achieving what i'm trying to explain but the main idea is to link the socket with an index (user id for example) in other to retrieve it later in the code.

Related