From af4ca0637a90628909a4970cc5ee16ff760d2ea9 Mon Sep 17 00:00:00 2001 From: inmake Date: Thu, 30 Oct 2025 18:07:36 +0500 Subject: [PATCH] Refactor SessionUsersPanel to optimize performance with useMemo for active cameras count, grid columns, grid rows, and active participants filtering. Update useWebRTC to prevent unnecessary state updates for participant stream, audio, video, and speaking state changes. Clean up SessionPage layout for consistency. --- client/src/components/SessionUsersPanel.tsx | 55 +++++++++++-------- client/src/hooks/useWebRTC.ts | 58 +++++++++++++-------- client/src/pages/SessionPage.tsx | 2 +- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/client/src/components/SessionUsersPanel.tsx b/client/src/components/SessionUsersPanel.tsx index 47313a1..d708f83 100644 --- a/client/src/components/SessionUsersPanel.tsx +++ b/client/src/components/SessionUsersPanel.tsx @@ -1,3 +1,4 @@ +import { useMemo } from "react"; import UserCamera from "./ui/UserCamera"; import UserDevicesControls from "./ui/UserDevicesControls"; import DraggableContainer from "./DraggableContainer"; @@ -32,26 +33,42 @@ function SessionUsersPanel({ updateSpeakingState?.(isSpeaking); }; - // Вычисляем количество камер для grid - const activeCamerasCount = - (localStream ? 8 : 0) + - participants.filter( - (p) => p.stream != null && p.stream.getTracks().length > 0 - ).length; + // Вычисляем количество камер для grid - мемоизируем для избежания лишних вычислений + const activeCamerasCount = useMemo( + () => + (localStream ? 1 : 0) + + participants.filter( + (p) => p.stream != null && p.stream.getTracks().length > 0 + ).length, + [localStream, participants] + ); + // Определяем количество колонок в зависимости от количества камер // 1-2 камеры: 1 колонка (друг под другом), 3-4: 2 колонки, 5-9: 3 колонки, 10-16: 4 колонки, 17-25: 5 колонок - const getGridColumns = (count: number): number => { - if (count <= 2) return 1; - if (count <= 4) return 2; - if (count <= 9) return 3; - if (count <= 16) return 4; + const gridColumns = useMemo(() => { + if (activeCamerasCount <= 2) return 1; + if (activeCamerasCount <= 4) return 2; + if (activeCamerasCount <= 9) return 3; + if (activeCamerasCount <= 16) return 4; return 5; - }; - - const gridColumns = getGridColumns(activeCamerasCount); + }, [activeCamerasCount]); // Вычисляем количество рядов для правильного расчета высоты - const gridRows = Math.ceil(activeCamerasCount / gridColumns); + const gridRows = useMemo( + () => Math.ceil(activeCamerasCount / gridColumns), + [activeCamerasCount, gridColumns] + ); + + // Фильтруем участников с активными потоками - мемоизируем для избежания лишних фильтраций + const activeParticipants = useMemo( + () => + participants.filter( + (participant) => + participant.stream != null && + participant.stream.getTracks().length > 0 + ), + [participants] + ); // Рендерим камеры const camerasContent = ( @@ -75,13 +92,7 @@ function SessionUsersPanel({ )} {/* Камеры удаленных участников - показываем только если есть поток с активными треками */} - {participants - .filter( - (participant) => - participant.stream != null && - participant.stream.getTracks().length > 0 - ) - .map((participant) => ( + {activeParticipants.map((participant) => ( { const [isConnected, setIsConnected] = useState(false); const [isInitialized, setIsInitialized] = useState(false); - // Мониторинг изменений участников - useEffect(() => { - console.log("[useWebRTC] Participants state updated:", participants.map(p => ({ - id: p.id, - hasStream: !!p.stream, - }))); - }, [participants]); - useEffect(() => { // Создаем сервис только один раз (синглтон) if (!webrtcServiceInstance) { @@ -71,6 +63,11 @@ export const useWebRTC = (roomId?: string, autoJoin = false) => { setParticipants((prev) => { const existing = prev.find((p) => p.id === participantId); if (existing) { + // Если поток уже тот же самый, не обновляем + if (existing.stream === stream) { + console.log("[useWebRTC] Stream already set for:", participantId); + return prev; + } console.log("[useWebRTC] Updating stream for existing participant:", participantId); return prev.map((p) => p.id === participantId ? { ...p, stream } : p @@ -100,26 +97,43 @@ export const useWebRTC = (roomId?: string, autoJoin = false) => { }, onParticipantAudioToggle: (participantId, isEnabled) => { console.log(`[useWebRTC] Audio toggle for ${participantId}: ${isEnabled}`); - setParticipants((prev) => - prev.map((p) => - p.id === participantId ? { ...p, isMuted: !isEnabled } : p - ) - ); + setParticipants((prev) => { + const participant = prev.find((p) => p.id === participantId); + const newMutedState = !isEnabled; + // Только обновляем, если значение действительно изменилось + if (participant && participant.isMuted === newMutedState) { + return prev; + } + return prev.map((p) => + p.id === participantId ? { ...p, isMuted: newMutedState } : p + ); + }); }, onParticipantVideoToggle: (participantId, isEnabled) => { console.log(`[useWebRTC] Video toggle for ${participantId}: ${isEnabled}`); - setParticipants((prev) => - prev.map((p) => - p.id === participantId ? { ...p, isVideoOff: !isEnabled } : p - ) - ); + setParticipants((prev) => { + const participant = prev.find((p) => p.id === participantId); + const newVideoOffState = !isEnabled; + // Только обновляем, если значение действительно изменилось + if (participant && participant.isVideoOff === newVideoOffState) { + return prev; + } + return prev.map((p) => + p.id === participantId ? { ...p, isVideoOff: newVideoOffState } : p + ); + }); }, onParticipantSpeakingChange: (participantId, isSpeaking) => { - setParticipants((prev) => - prev.map((p) => + setParticipants((prev) => { + const participant = prev.find((p) => p.id === participantId); + // Только обновляем, если значение действительно изменилось + if (participant && participant.isSpeaking === isSpeaking) { + return prev; + } + return prev.map((p) => p.id === participantId ? { ...p, isSpeaking } : p - ) - ); + ); + }); }, onChatMessage: (message) => { console.log("[useWebRTC] onChatMessage called:", message); diff --git a/client/src/pages/SessionPage.tsx b/client/src/pages/SessionPage.tsx index 40cd59a..b5e1827 100644 --- a/client/src/pages/SessionPage.tsx +++ b/client/src/pages/SessionPage.tsx @@ -166,7 +166,7 @@ function SessionPage() { session.mode === "stream" && session.server?.localIp && session.playerPort && ( -
+