我有以下反应组件
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钩子定义的参与者的hashTable。
然后,我使用useEffect来监听聊天室的套接字事件—只有4个事件
然后在那之后,我定义了4个回调处理程序,这些事件与服务器上执行的顺序有关
最后,我有另一个回调函数,它传递给列表中的每个子参与者,以便在子组件创建其rtcPeer对象后,将其发送给父组件,以便在参与者的hashTable中的参与者对象上设置它
流程如下:参与者加入房间-
第一个事件状态为空,随后的两个事件状态为空,然后它再次为空,此模式不断重复一个空状态,然后接下来的两个是正确的,我不知道状态发生了什么
困难的是,您遇到了几个相互影响的问题,这些问题使您的故障排除变得混乱。
最大的问题是您正在设置多个套接字事件处理程序。每次重新渲染,您都在调用socket.on
而没有调用socket.off
。
关于如何处理这一问题,我可以想象有三种主要方法:
>
设置单个套接字事件处理程序,并且只对参与者
状态使用功能更新。使用这种方法,您将使用一个空的依赖数组来表示use效应
,并且不会在效果中的任何地方引用参与者
(包括消息处理程序调用的所有方法)。如果您引用参与者
,那么一旦第一次重新渲染发生,您将引用它的旧版本。如果参与者
需要发生的更改可以使用功能更新轻松完成,那么这可能是最简单的方法。
为参与者的每次更改设置一个新的套接字事件处理程序。为了使其正常工作,您需要删除以前的事件处理程序,否则您将拥有与呈现相同数量的事件处理程序。当您有多个事件处理程序时,创建的第一个事件处理程序将始终使用第一个版本的
参与者
(空),第二个事件处理程序将始终使用第二个版本的参与者
,等等。这将起作用,并为您如何使用现有的参与者
状态提供更多的灵活性,但是有一个缺点,就是反复拆卸和设置套接字事件处理程序,这让人感觉很笨拙。
设置单个套接字事件处理程序,并使用ref访问当前的
参与者
状态。这与第一种方法类似,但添加了一个附加效果,该效果在每次渲染时执行,以将当前参与者
状态设置为ref,以便消息处理程序可以可靠地访问该状态。
无论使用哪种方法,如果您将消息处理程序移出呈现函数并显式传递其依赖项,我认为您都可以更轻松地推理代码正在执行的操作。
第三个选项提供了与第二个选项相同的灵活性,同时避免了重复设置套接字事件处理程序,但在管理
参与者sref
时增加了一点复杂性。
下面是第三个选项的代码(我没有尝试执行这个,所以我不能保证我没有轻微的语法问题):
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] = React.useState({});
console.log('Participants -> ', participants);
const participantsRef = React.useRef(participants);
React.useEffect(() => {
// This effect executes on every render (no dependency array specified).
// Any change to the "participants" state will trigger a re-render
// which will then cause this effect to capture the current "participants"
// value in "participantsRef.current".
participantsRef.current = participants;
});
React.useEffect(() => {
// This effect only executes on the initial render so that we aren't setting
// up the socket repeatedly. This means it can't reliably refer to "participants"
// because once "setParticipants" is called this would be looking at a stale
// "participants" reference (it would forever see the initial value of the
// "participants" state since it isn't in the dependency array).
// "participantsRef", on the other hand, will be stable across re-renders and
// "participantsRef.current" successfully provides the up-to-date value of
// "participants" (due to the other effect updating the ref).
const handler = (message) => {messageHandler(message, participantsRef.current, setParticipants)};
socket.on('message', handler);
return () => {
socket.off('message', handler);
}
}, []);
return (
<div id="meetingRoom">
{Object.values(participants).map(participant => (
<Participant
key={participant.id}
participant={participant}
roomName={roomName}
setRtcPeerForUser={setRtcPeerForUser}
sendMessage={sendMessage}
/>
))}
</div>
);
}
此外,下面是一个工作示例,模拟上述代码中发生的情况,但不使用
socket
,以清楚显示使用参与者
与参与者SREF
之间的区别。观察控制台并单击这两个按钮,查看将参与者
传递给消息处理程序的两种方式之间的差异。
import React from "react";
const messageHandler = (participantsFromRef, staleParticipants) => {
console.log(
"participantsFromRef",
participantsFromRef,
"staleParticipants",
staleParticipants
);
};
export default function ConferencingRoom() {
const [participants, setParticipants] = React.useState(1);
const participantsRef = React.useRef(participants);
const handlerRef = React.useRef();
React.useEffect(() => {
participantsRef.current = participants;
});
React.useEffect(() => {
handlerRef.current = message => {
// eslint will complain about "participants" since it isn't in the
// dependency array.
messageHandler(participantsRef.current, participants);
};
}, []);
return (
<div id="meetingRoom">
Participants: {participants}
<br />
<button onClick={() => setParticipants(prev => prev + 1)}>
Change Participants
</button>
<button onClick={() => handlerRef.current()}>Send message</button>
</div>
);
}
我正在开发一个在线的,基于文本的RPG(Github);目前我有一个PHP后端,它将会话数据存储在redis服务器中。对于需要实时通信的所有内容(聊天,消息传递和连接用户列表),我使用Node.js和 socket.io 用于websocket。 我目前在我的网络索克服务器上有3个命名空间: 消息服务器 在线服务器 聊天服务器 我已经让它工作了,但恐怕大部分是由“黑客”制作的。现在,我无法向特定的
我是Heroku的新手,我正在尝试部署使用socket.io侦听的NodeJS应用程序。我能够部署到本地和它的工作罚款。但是当在Heroku中运行相同的内容时,它没有响应。 在Heroku中有没有需要为套接字io指定的特定端口?我不能使用process.env.port,因为我正在将其用于app server端口。 有什么解决这个问题的想法吗?或者我们需要启用一些配置来使用套接字IO吗? 我想我们
我正在努力正确配置nginx,以确保它可以处理Express(端口8081)和Socket的代理。io(端口3000)。下面是我的配置,它当前为整个请求(不仅仅是Socket.io)产生了502错误: 据我所知,我需要确保Socket使用的Websocket。io已升级到HTTP,但这正是我努力掌握自己需要做什么的地方。可能是两个插座。io和Express需要在不同的端口上运行,然后需要根据我上面
我面临的问题是: < li >当我加载页面时,服务器的控制台中会显示套接字(我可以获取远程地址和远程端口) < li >无论我做什么,客户端中的连接总是挂起。我尝试了Chrome、Brave、Mozilla和Safari,但似乎没有一个能用。 我错过了什么?我试图在连接准备好之前不发送消息,但我从未在我的PC中获得过该状态。即使我在没有事件的情况下建立onopen属性,客户端中的警报也不会弹出。
问题内容: 假设我有一个Java程序,该程序使用HTTP 1.1在服务器上发出HTTP请求,并且不关闭连接。我提出一个请求,并读取从绑定到套接字的输入流返回的所有数据。但是,在发出第二个请求时,服务器没有响应(或者流有问题- 它不再提供任何输入)。如果我按顺序发出请求(请求,请求,读取),则可以正常工作,但(请求,读取,请求,读取)则不能。 有人能解释为什么会发生这种情况吗?(代码片段如下)。无论
问题内容: 我正在尝试新的React Hooks,并有一个带有计数器的Clock组件,该计数器应该每秒增加一次。但是,该值不会增加到超过一。 问题答案: 原因是传递给的闭包中的回调仅访问第一个渲染器中的变量,而无法访问后续渲染器中的新值,因为第二次未调用。 回调中的值始终为0 。 就像您熟悉的状态挂钩一样,状态挂钩有两种形式:一种是处于更新状态的状态,另一种是将当前状态传入的回调形式。您应该使用第