Сложность в том, что у вас было несколько проблем, взаимодействующих друг с другом, которые приводили к путанице при устранении неполадок.
Самая большая проблема заключается в том, что вы настраиваете несколько обработчиков событий сокетов.При каждом повторном рендеринге вы звоните socket.on
, даже не позвонив socket.off
.
Вам необходимо использовать один из двух подходов:
Установить обработчик событий одного сокета.При таком подходе вы использовали бы пустой массив зависимостей для useEffect
, но это означает, что вы не можете ссылаться на participants
в любом месте внутри вашего эффекта (включая все методы, вызываемые вашим обработчиком сообщений),Если вы сделаете ссылку participants
, вы будете ссылаться на старую версию, как только произойдет первый повторный рендеринг.
Установите новый обработчик событий сокета с каждым изменением на participants
.Для правильной работы необходимо удалить предыдущий обработчик событий , в противном случае у вас будет такое же количество обработчиков событий, что и для визуализации.Когда у вас есть несколько обработчиков событий, первый созданный будет всегда использовать первую версию participants
(пусто), второй всегда будет использовать вторую версию participants
и т. Д.
В любом случае, я думаю, вам будет легче рассуждать о том, что делает код, если вы переместите свой обработчик сообщений из функции рендеринга и явно передадите его зависимости.
Второйопция более гибкая для работы, поскольку позволяет работать с participants
вне функционального обновления setParticipants
, поэтому я просто покажу эту опцию.С этой опцией нет необходимости использовать синтаксис функционального обновления с setParticipants
, но вам нужно всегда удалять предыдущий обработчик событий, используя метод очистки, возвращаемый из useEffect
.
Вот чтокод будет выглядеть как со вторым вариантом (я не пытался выполнить это, поэтому я не даю никаких гарантий, что у меня нет незначительных синтаксических проблем):
const messageHandler = (message, participants, setParticipants) => {
console.log('Message received: ' + message.event);
const onExistingParticipants = (userid, existingUsers) => {
console.log('onExistingParticipants Called!!!!!');
//Add local User
const user = {
id: userid,
username: userName,
published: true,
rtcPeer: null
};
setParticipants({
...participants,
[user.id]: user
});
existingUsers.forEach(function (element) {
receiveVideo(element.id, element.name)
})
};
const onReceiveVideoAnswer = (senderid, sdpAnswer) => {
console.log('participants in Receive answer -> ', participants);
console.log('***************')
// participants[senderid].rtcPeer.processAnswer(sdpAnswer)
};
const addIceCandidate = (userid, candidate) => {
console.log('participants in Receive canditate -> ', participants);
console.log('***************');
// participants[userid].rtcPeer.addIceCandidate(candidate)
};
const receiveVideo = (userid, username) => {
console.log('Received Video Called!!!!');
//Add remote User
const user = {
id: userid,
username: username,
published: false,
rtcPeer: null
};
setParticipants({
...participants,
[user.id]: user
});
};
//Callback for setting rtcPeer after creating it in child component
const setRtcPeerForUser = (userid, rtcPeer) => {
setParticipants({
...participants,
[userid]: {...participants[userid], rtcPeer: rtcPeer}
});
};
switch (message.event) {
case 'newParticipantArrived':
receiveVideo(message.userid, message.username);
break;
case 'existingParticipants':
onExistingParticipants(
message.userid,
message.existingUsers
);
break;
case 'receiveVideoAnswer':
onReceiveVideoAnswer(message.senderid, message.sdpAnswer);
break;
case 'candidate':
addIceCandidate(message.userid, message.candidate);
break;
default:
break;
}
};
function ConferencingRoom() {
const [participants, setParticipants] = useState({});
console.log('Participants -> ', participants);
useEffect(() => {
const handler = (message) => {messageHandler(message, participants, setParticipants)};
socket.on('message', handler);
return () => {
// THIS IS THE IMPORTANT CHANGE
socket.off('message', handler);
}
}, [participants]);
return (
<div id="meetingRoom">
{Object.values(participants).map(participant => (
<Participant
key={participant.id}
participant={participant}
roomName={roomName}
setRtcPeerForUser={setRtcPeerForUser}
sendMessage={sendMessage}
/>
))}
</div>
);
}