我有如下的React组分
function ConferencingRoom() {
const [participants, setParticipants] = useState({})
console.log('Participants -> ', participants)
useEffect(() => {
// messages handlers
socket.on('message', message => {
console.log('Message received: ' + message.event)
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
}
})
return () => {}
}, [participants])
// Socket Connetction handlers functions
const onExistingParticipants = (userid, existingUsers) => {
console.log('onExistingParticipants Called!!!!!')
//Add local User
const user = {
id: userid,
username: userName,
published: true,
rtcPeer: null
}
setParticipants(prevParticpants => ({
...prevParticpants,
[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(prevParticpants => ({
...prevParticpants,
[user.id]: user
}))
}
//Callback for setting rtcPeer after creating it in child component
const setRtcPeerForUser = (userid, rtcPeer) => {
setParticipants(prevParticpants => ({
...prevParticpants,
[userid]: { ...prevParticpants[userid], rtcPeer: rtcPeer }
}))
}
return (
<div id="meetingRoom">
{Object.values(participants).map(participant => (
<Participant
key={participant.id}
participant={participant}
roomName={roomName}
setRtcPeerForUser={setRtcPeerForUser}
sendMessage={sendMessage}
/>
))}
</div>
)
}
它所具有的唯一状态是调用内部的参与者的哈希表,使用useState钩子来定义它。
然后我使用useEffect监听聊天室的套接字事件,只有4个事件
然后,根据这些事件在服务器上的执行顺序,为这些事件定义4个回调处理程序
最后,我还有另一个回调函数,它被传递给列表中的每个子参与者,以便在子组件创建其rtcPeer对象之后,将其发送给父组件,以便在参与者的hashTable中的参与者对象上设置该对象
流程如下:参与者加入会议室-〉调用existingParticipants事件-〉创建本地参与者并将其添加到参与者hashTable,然后-〉recieveVideoAnswer,服务器多次发出candidate,如您在屏幕截图中所见
第一个事件状态是空的,接下来的两个事件是空的,然后又是空的,这个模式不断重复一个空的状态,然后接下来的两个是正确的,我不知道这个状态是怎么回事
2条答案
按热度按时间lnlaulya1#
这方面的困难之处在于,您遇到了几个相互影响的问题,这些问题会使您的故障排除感到困惑。
最大的问题是你设置了多个套接字事件处理器,每次重新呈现时,你都在调用
socket.on
,而从来没有调用过socket.off
。对于如何处理这个问题,我可以设想三种主要的方法:
participants
状态使用函数更新。使用这种方法,您将对useEffect
使用一个空的依赖项数组。并且您不会在效果中的任何地方**引用participants
(包括消息处理程序调用的所有方法)。如果你引用participants
,那么在第一次重新调用participants
时,你将引用它的旧版本。如果需要对participants
进行的更改可以使用函数更新轻松完成,那么这可能是最简单的方法。participants
进行更改时,请设置一个新的套接字事件处理程序。为了使其正常工作,您需要删除以前的事件处理程序否则您将拥有与呈现器相同数量的事件处理程序。如果您有多个事件处理程序,则创建的第一个事件处理程序将始终使用participants
的第一个版本(空),第二个将始终使用participants
的第二个版本,等等。这将工作,并提供更大的灵活性,在如何使用现有的participants
状态,但有一个缺点,反复拆除和设置套接字事件处理程序,这感觉笨重。participants
状态。这与第一种方法类似,但添加了一个附加效果,该效果在每次呈现时执行,以将当前participants
状态设置为ref,以便消息处理程序可以可靠地访问它。无论您使用哪种方法,我认为如果您将消息处理程序移出呈现函数并显式传入其依赖项,您将更容易推理代码正在做什么。
第三个选项提供了与第二个选项相同的灵活性,同时避免了套接字事件处理程序的重复设置,但增加了管理
participantsRef
的复杂性。下面是使用第三个选项时的代码(我还没有尝试执行它,所以我不能保证我没有小的语法问题):
另外,下面是一个模拟上述代码中发生的情况的工作示例,但未使用
socket
,以便清楚地显示使用participants
与participantsRef
之间的区别。观察控制台并单击两个按钮,以查看将participants
传递到消息处理程序的两种方法之间的区别。emeijp432#
创建一个单例
socket
对象,并在闭包的帮助下在任何其他处理程序中使用该示例。