我在我的 useEffect 挂钩之一中使用了此代码片段。但它会改变对象并向我显示要出现两次的消息。
setConversations((prev) => {
const oldConversation = room in prev ? prev[room] : [];
const newConversation = [
...oldConversation,
{
sender: from,
message: msg,
time: new Date().toLocaleString(),
},
];
return { ...prev, [room]: newConversation };
});
我错过了什么?任何建议将不胜感激。
编辑:
自定义效果(接收消息的地方):
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]);
};
发送消息功能:
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 };
});
};
注意:在App.js文件中使用了自定义钩子。
谷歌搜索了一段时间后,我发现了一个在 useEffect 逻辑中处理双重渲染的 hack。可能存在其他方法来解决我的特定问题,但我发现的黑客对我来说效果很好。 由于react-18在开发模式下渲染了两次useEffect钩子,禁用严格模式不会使渲染一次。为此,我们需要使用布尔变量来处理这种情况。
useEffect(() => {
let ignore = false;
fetchStuff().then(res => {
if (!ignore) setResult(res)
})
return () => { ignore = true }
}, [])
有关更多详细信息,请查看this博客文章。