#reactjs #next.js #use-ref
Вопрос:
У меня есть СЛЕДУЮЩЕЕ приложение JS, которое использует следующий VideoPlayer
компонент:
import React, { useCallback, useEffect, useState, useContext } from "react"; import { useSocket, useSocketUpdate } from "../Store"; import { useRouter } from "next/router"; import styles from "../styles/CallPage.module.css"; const VideoPlayer = (props: any) =gt; { const { isAdmin, myVideo, userVideo }: any = useSocket(); const router = useRouter(); useEffect(() =gt; { if (router.isReady amp;amp; myVideo.current amp;amp; userVideo.current) { myVideo?.current.load(); userVideo?.current.load(); } }, []); return ( lt;div className={styles.grid_container}gt; {stream amp;amp; ( lt;divgt; lt;video className={styles.video} playsInline muted ref={myVideo} autoPlay /gt; lt;div className={styles.overlay}gt; lt;h2 className={styles.overlay_h2}gt;OVERLAYlt;/h2gt; lt;/divgt; lt;/divgt; )} {callAccepted amp;amp; !callEnded ? ( lt;divgt; lt;video className={styles.video} playsInline ref={userVideo} autoPlay /gt; lt;div className={styles.overlay}gt; lt;h2 className={styles.overlay_h2}gt;OVERLAY2lt;/h2gt; lt;/divgt; lt;/divgt; ) : ( isGuest amp;amp; !callAccepted amp;amp; ( lt;div className={styles.grid_container}gt; lt;div className={styles.no_match_content}gt; lt;h2 className={styles.h2_no_match}gt; Please wait until the host of this room let you in. lt;/h2gt; lt;div className={styles.btn_no_match}gt;lt;/divgt; lt;/divgt; lt;/divgt; ) )} lt;/divgt; ); }; export default VideoPlayer;
Ссылка на MyVideo извлекается с помощью крючка useSocket, который взят из следующего Store
объявления:
import React, { createContext, useState, useRef, useEffect, useContext } from "react"; import { io } from "socket.io-client"; import Peer from "simple-peer"; import { useRouter } from "next/router"; const SocketContext = createContext({}); const SocketUpdateContext = createContext({}); const socket = io(`${process.env.NEXT_PUBLIC_SERVER_URL}`); function useSocket() { const useSocket = useContext(SocketContext); if (useSocket === undefined) { throw new Error("useSocket must be used within a SocketProvider"); } return useSocket; } function useSocketUpdate() { const useSocketUpdate = useContext(SocketUpdateContext); if (useSocketUpdate === undefined) { throw new Error("useSocketUpdate must be used within a SocketProvider"); } return useSocketUpdate; } const ContextProvider = ({ children }: any) =gt; { const router = useRouter(); const [callAccepted, setCallAccepted] = useState(false); const [callEnded, setCallEnded] = useState(false); const [stream, setStream] = useStatelt;MediaStreamgt;(); const [name, setName] = useState(""); const [call, setCall] = useStatelt;anygt;({}); const [me, setMe] = useState(""); const [isAdmin, setAdmin] = useState(false); const [isGuest, setGuest] = useState(false); const [idToCall, setIdToCall] = useState(""); const myVideo = useReflt;HTMLVideoElementgt;(null!); const userVideo = useReflt;HTMLVideoElementgt;(null!); const connectionRef = useReflt;Peer.Instancegt;(null!); useEffect(() =gt; { navigator.mediaDevices .getUserMedia({ video: true, audio: true }) .then((currentStream) =gt; { setStream(currentStream); if (myVideo.current) { myVideo.current.srcObject = currentStream; } }); socket.on("me", (id) =gt; { setMe(id); }); socket.on("callUser", ({ from, name: callerName, signal }) =gt; { setCall({ isReceivingCall: true, from, name: callerName, signal }); }); }, []); const answerCall = () =gt; { setCallAccepted(true); const peer = new Peer({ initiator: false, trickle: false, stream }); peer.on("signal", (data) =gt; { socket.emit("answerCall", { signal: data, to: call.from }); }); peer.on("stream", (currentStream) =gt; { if (userVideo.current) userVideo.current.srcObject = currentStream; }); peer.signal(call.signal); connectionRef.current = peer; }; const callUser = (id: String) =gt; { // setAdmin(false); const peer = new Peer({ initiator: true, trickle: false, stream }); peer.on("signal", async (data) =gt; { socket.emit("callUser", { userToCall: id, signalData: data, from: me, name }); }); peer.on("stream", (currentStream) =gt; { if (userVideo.current) userVideo.current.srcObject = currentStream; }); socket.on("callAccepted", (signal) =gt; { setCallAccepted(true); peer.signal(signal); }); if (connectionRef.current) connectionRef.current = peer; }; const leaveCall = () =gt; { setCallEnded(true); setAdmin(false); if (connectionRef.current) connectionRef.current.destroy(); // if (typeof window !== "undefined") window.location.reload(); router.push("/home"); }; return ( lt;SocketContext.Provider value={{ call, callAccepted, myVideo, userVideo, stream, name, callEnded, me, isAdmin, router, isGuest, user, isAuthenticated, isUnauthenticated, isAuthenticating, authError, idToCall }} gt; lt;SocketUpdateContext.Provider value={{ callUser, leaveCall, answerCall, setName, setAdmin, setGuest, logout, authenticate, signup, login, setIdToCall }} gt; {children} lt;/SocketUpdateContext.Providergt; lt;/SocketContext.Providergt; ); }; export { ContextProvider, SocketContext, useSocket, useSocketUpdate };
And finally Im rendering the VideoPlayer component in this CallPage
:
import { useEffect, useReducer, useState, useContext } from "react"; import styles from "../styles/CallPage.module.css"; import Peer from "simple-peer"; import VideoPlayer from "../components/VideoPlayer"; import Page from "../components/UI/Page"; import Link from "next/link"; import Head from "next/head"; import { useSocket, useSocketUpdate } from "../Store"; const initialState = []; const CallPage = () =gt; { const { isAdmin, myVideo, isAuthenticated, isUnauthenticated, user }: any = useSocket(); if (isUnauthenticated) { return ( lt;Pagegt; lt;div className={styles.no_match_content}gt; lt;h2 className={styles.h2_no_match}gt; Please login first to access a call. lt;/h2gt; lt;div className={styles.btn_no_match}gt; lt;Link passHref href="/login"gt; Login lt;/Linkgt; lt;/divgt; lt;/divgt; lt;/Pagegt; ); } let alertTimeout = null; const [messageList, messageListReducer] = useReducer( MessageListReducer, initialState ); useEffect(() =gt; { if (isAdmin) { setMeetInfoPopup(true); } }, []); return ( lt;div className={styles.page_container}gt; lt;VideoPlayer className={styles.video_container} /gt; )} lt;/divgt; ); }; export default CallPage;
Когда я нажимаю на свой дом, чтобы перейти на страницу вызовов, делая это:
lt;Link passHref href="/callpage" gt; lt;Button loadingText="Loading" colorScheme="teal" variant="outline" spinnerPlacement="start" className={styles.btn} onClick={handleAdmin} gt; New Meeting lt;/Buttongt; lt;/Linkgt;
Маршрутизатор берет меня, и все отображается, кроме видеоряда. Если я обновлю страницу, она загрузится. У вас есть какие-нибудь идеи, почему это происходит? Devtools выдает ошибку, в которой говорится, что
функциональным компонентам не могут быть даны ссылки
даже несмотря на то, что я использую такую ссылку в элементе DOM, как вы можете видеть. Поэтому я уже исключил использование forwardref. Я был бы очень признателен за любую помощь. Я тут ничего не понимаю.
Комментарии:
1. Функциональные компоненты не принимают ссылки React, вам необходимо переслать все переданные ссылки React. Какому компоненту(компонентам) передаются ссылки, и какая ссылка это?
Ответ №1:
Одна идея…
useEffect(() =gt; { if (router.isReady amp;amp; myVideo.current amp;amp; userVideo.current) { myVideo?.current.load(); userVideo?.current.load(); } }, []);
У вашего крючка есть пустой массив зависимостей. Конечно, возможно, что при первом отображении router.isReady
результаты будут ложными. Вы должны перейти router.isReady
к массиву зависимостей.
Кроме того, ваша функция CallPage условно запускает эффект. Этот эффект:
useEffect(() =gt; { if (isAdmin) { setMeetInfoPopup(true); } }, []);
следует также передать isAdmin
и независимо от ЗНАЧЕНИЯ setMeetInfoPopup
задатчика в массив зависимостей. Предполагая useState
, что значение для этого сеттера вызывается meetInfoPopup
, крючок на самом деле должен выглядеть так:
useEffect(() =gt; { if (isAdmin amp;amp; !meetInfoPopup) { setMeetInfoPopup(true); } }, [isAdmin, meetInfoPopup]);