我有以下反应组件
function ConferencingRoom(){ const [participant,setParticipants] = useState({}) console.log(‘参与者 - >’,参与者)
useEffect(()=> …
关于这一点的困难在于,您有几个问题彼此交互,这使您的故障排除混乱。
最大的问题是您正在设置多个套接字事件处理程序。每次重新渲染,你都在呼唤 socket.on 没有打过电话 socket.off 。
socket.on
socket.off
您需要使用以下两种方法之一:
设置单个套接字事件处理程序。使用此方法,您将使用空的依赖关系数组 useEffect ,但这意味着你无法参考 participants 的 随地 强> 在你的效果范围内(包括你的消息处理程序调用的所有方法)。如果你参考 participants 一旦第一次重新渲染,你将引用它的旧版本。
useEffect
participants
设置一个新的套接字事件处理程序,每次更改为 participants 。为了使其正常工作, 的 你需要删除以前的事件处理程序 强> 否则,您将拥有与渲染相同数量的事件处理程序。当您有多个事件处理程序时,创建的第一个事件处理程序将始终使用第一个版本 participants (空),第二个将永远使用第二个版本 participants 等
在任何一种情况下,如果您将消息处理程序移出渲染函数并显式传递其依赖项,我认为您将更容易理解代码正在执行的操作。
第二种选择更灵活,因为它可以处理 participants 在...之外 setParticipants 功能更新,所以我只会显示该选项。使用此选项,不必使用功能更新语法 setParticipants ,但您需要始终使用从中返回的清理方法删除以前的事件处理程序 useEffect 。
setParticipants
以下是使用第二个选项时代码的样子(我没有尝试执行此操作,因此我不保证我没有轻微的语法问题):
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> ); }