I used this code snippet inside one of my useEffect hooks. But it mutates the object and shows me the message to be appeared twice.
setConversations((prev) => {
const oldConversation = room in prev ? prev[room] : [];
const newConversation = [
...oldConversation,
{
sender: from,
message: msg,
time: new Date().toLocaleString(),
},
];
return { ...prev, [room]: newConversation };
});
What am I missing? Any suggestions would be much appreciated.
Edit:
The custom effect (where the message is received):
export const useSocketEventListener = (
socket,
peer,
setPeersOnConference,
setPeerIdsOnConference,
setAvailableUsers,
availableRooms,
setAvailableRooms,
setConferenceId,
setCallOthersTriggered,
setTransited,
calls,
setCalls,
setConversations
) => {
useEffect(() => {
socket?.on('receiveMessage', (msg, from, room) => {
// alert(`${msg} from ${from} room ${room}`);
// let temp = socket.id === room ? from : room;
setConversations((prev) => { // called twice (expected once to add the message to the state)
const oldConversation = room in prev ? prev[room] : [];
const newConversation = [
...oldConversation,
{
sender: from,
message: msg,
time: new Date().toLocaleString(),
},
];
console.log('calling once');
return { ...prev, [room]: newConversation };
});
console.log('i fire once');
});
socket?.on('joinRoomAlert', (socketId, room) => {
//
});
socket?.on('leaveRoomAlert', (socketId, room) => {
alert(`${socketId} left the room ${room}`);
});
socket?.on('receivePeersOnConference', (peersOnConference) => {
// setPeersOnConference(peersOnConference);
});
socket?.on('receiveData', (data) => {
setAvailableUsers(data.users);
setAvailableRooms(data.rooms);
});
socket?.on('peerEndCall', (peerId) => {
calls[peerId]?.close();
setCalls((prev) =>
Object.keys(prev)
.filter((key) => key !== peerId)
.reduce((obj, key) => ({ ...obj, [key]: prev[key] }), {})
);
setPeersOnConference((prev) => {
if (Object.keys(prev).length === 1) {
setCallOthersTriggered(false);
setTransited(false);
return {};
}
return Object.keys(prev)
.filter((key) => key !== peerId)
.reduce((obj, key) => ({ ...obj, [key]: prev[key] }), {});
});
});
socket?.on(
'receiveCallOthersTriggered',
(peerIds, conferenceId, caller) => {
console.log('peerIds: ', peerIds);
setConferenceId(socket.id === conferenceId ? caller : conferenceId);
setCallOthersTriggered(true);
setPeerIdsOnConference([...peerIds]);
}
);
socket?.on('leaveCallAlert', (leftPeerId) => {
//
});
socket?.emit('fetchData');
peer?.on('call', async (call) => {
try {
setTransited(true);
const selfStream = await getMedia();
console.log(peer.id, selfStream);
setPeersOnConference((prev) => ({ ...prev, [peer.id]: selfStream }));
call.answer(selfStream);
call.on('stream', (remoteStream) => {
console.log(call.peer, remoteStream);
setPeersOnConference((prev) => ({
...prev,
[call.peer]: remoteStream,
}));
});
call.on('close', () => {
selfStream.getTracks().forEach((track) => track.stop());
});
call.on('error', (e) => console.log('error in peer call'));
} catch (e) {
console.log('error while receiving call');
}
});
}, [socket, peer]);
};
The function to send message:
export const sendMessage = (socket, msg, to, setMessage, setConversations) => {
socket.emit('sendMessage', msg, to);
setMessage('');
setConversations((prev) => { // called once
const oldConversation = to in prev ? prev[to] : [];
const newConversation = [
...oldConversation,
{
sender: socket.id,
message: msg,
time: new Date().toLocaleString(),
},
];
return { ...prev, [to]: newConversation };
});
};
Note: Used the custom hook in the App.js file.
After googling for a while, I found a hack to handle double render in useEffect logic. There may exist other ways to solve my specific problem but the hack I found will do fine for me. As react-18 renders the useEffect hook twice in development mode, disabling the strict mode won't make the render once. For which we need to use a boolean variable to handle such cases.
For more details, check out this blog post.
Note: The main problem was not about mutation. Updating function is working totally fine. Rendering the useEffect twice caused the error to be occured. :)