视频ref只在刷新后呈现+函数组件不能给出ref



我有一个使用以下VideoPlayer的NEXT JS应用程序组件:

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) => {
const {
isAdmin,
myVideo,
userVideo
}: any = useSocket();
const router = useRouter();
useEffect(() => {
if (router.isReady && myVideo.current && userVideo.current) {
myVideo?.current.load();
userVideo?.current.load();
}
}, []);
return (
<div className={styles.grid_container}>
{stream && (
<div>
<video
className={styles.video}
playsInline
muted
ref={myVideo}
autoPlay
/>
<div className={styles.overlay}>
<h2 className={styles.overlay_h2}>OVERLAY</h2>
</div>
</div>
)}
{callAccepted && !callEnded ? (
<div>
<video
className={styles.video}
playsInline
ref={userVideo}
autoPlay
/>
<div className={styles.overlay}>
<h2 className={styles.overlay_h2}>OVERLAY2</h2>
</div>
</div>
) : (
isGuest &&
!callAccepted && (
<div className={styles.grid_container}>
<div className={styles.no_match_content}>
<h2 className={styles.h2_no_match}>
Please wait until the host of this room let you in.
</h2>
<div className={styles.btn_no_match}></div>
</div>
</div>
)
)}
</div>
);
};
export default VideoPlayer;

myVideo ref正在被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) => {
const router = useRouter();
const [callAccepted, setCallAccepted] = useState(false);
const [callEnded, setCallEnded] = useState(false);
const [stream, setStream] = useState<MediaStream>();
const [name, setName] = useState("");
const [call, setCall] = useState<any>({});
const [me, setMe] = useState("");
const [isAdmin, setAdmin] = useState(false);
const [isGuest, setGuest] = useState(false);
const [idToCall, setIdToCall] = useState("");
const myVideo = useRef<HTMLVideoElement>(null!);
const userVideo = useRef<HTMLVideoElement>(null!);
const connectionRef = useRef<Peer.Instance>(null!);
useEffect(() => {
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((currentStream) => {
setStream(currentStream);
if (myVideo.current) {
myVideo.current.srcObject = currentStream;
}
});
socket.on("me", (id) => {
setMe(id);
});
socket.on("callUser", ({ from, name: callerName, signal }) => {
setCall({ isReceivingCall: true, from, name: callerName, signal });
});
}, []);
const answerCall = () => {
setCallAccepted(true);
const peer = new Peer({ initiator: false, trickle: false, stream });
peer.on("signal", (data) => {
socket.emit("answerCall", { signal: data, to: call.from });
});
peer.on("stream", (currentStream) => {
if (userVideo.current) userVideo.current.srcObject = currentStream;
});
peer.signal(call.signal);
connectionRef.current = peer;
};
const callUser = (id: String) => {
// setAdmin(false);
const peer = new Peer({ initiator: true, trickle: false, stream });
peer.on("signal", async (data) => {
socket.emit("callUser", {
userToCall: id,
signalData: data,
from: me,
name
});
});
peer.on("stream", (currentStream) => {
if (userVideo.current) userVideo.current.srcObject = currentStream;
});
socket.on("callAccepted", (signal) => {
setCallAccepted(true);
peer.signal(signal);
});
if (connectionRef.current) connectionRef.current = peer;
};
const leaveCall = () => {
setCallEnded(true);
setAdmin(false);
if (connectionRef.current) connectionRef.current.destroy();
// if (typeof window !== "undefined") window.location.reload();
router.push("/home");
};
return (
<SocketContext.Provider
value={{
call,
callAccepted,
myVideo,
userVideo,
stream,
name,
callEnded,
me,
isAdmin,
router,
isGuest,
user,
isAuthenticated,
isUnauthenticated,
isAuthenticating,
authError,
idToCall
}}
>
<SocketUpdateContext.Provider
value={{
callUser,
leaveCall,
answerCall,
setName,
setAdmin,
setGuest,
logout,
authenticate,
signup,
login,
setIdToCall
}}
>
{children}
</SocketUpdateContext.Provider>
</SocketContext.Provider>
);
};
export { ContextProvider, SocketContext, useSocket, useSocketUpdate };

最后我在这个CallPage中渲染VideoPlayer组件:

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 = () => {
const { isAdmin, myVideo, isAuthenticated, isUnauthenticated, user }: any =
useSocket();
if (isUnauthenticated) {
return (
<Page>
<div className={styles.no_match_content}>
<h2 className={styles.h2_no_match}>
Please login first to access a call.
</h2>
<div className={styles.btn_no_match}>
<Link passHref href="/login">
Login
</Link>
</div>
</div>
</Page>
);
}
let alertTimeout = null;
const [messageList, messageListReducer] = useReducer(
MessageListReducer,
initialState
);    
useEffect(() => {
if (isAdmin) {
setMeetInfoPopup(true);
}
}, []);   
return (
<div className={styles.page_container}>
<VideoPlayer className={styles.video_container} />
)}
</div>
);
};
export default CallPage;

当我点击主页转到CallPage时,这样做:

<Link
passHref
href="/callpage"
>
<Button
loadingText="Loading"
colorScheme="teal"
variant="outline"
spinnerPlacement="start"
className={styles.btn}
onClick={handleAdmin}
>
New Meeting
</Button>
</Link>

路由器带着我,所有的东西都被渲染了,除了VideoRef。如果我刷新页面,它就会加载。你知道为什么会这样吗?devtools抛出一个错误,说

功能组件不能给出refs

,即使我在DOM元素中使用这样的ref,你可以看到。所以我已经排除了使用forwardref。我将非常感谢任何帮助。我一点头绪都没有。

一个想法…

useEffect(() => { 
if (router.isReady && myVideo.current && userVideo.current) {
myVideo?.current.load();
userVideo?.current.load();
}
}, []);

钩子有一个空的依赖数组。当然有可能在第一次渲染router.isReady时返回false。你应该把router.isReady传递给依赖数组。

另外,你的CallPage函数有条件地运行一个效果。效果:

useEffect(() => {
if (isAdmin) {
setMeetInfoPopup(true);
}
}, []); 

还应该将isAdminsetMeetInfoPopupsetter的VALUE传递到依赖数组中。假设该setter的useState值被称为meetInfoPopup,钩子实际上应该是这样读的:

useEffect(() => {
if (isAdmin && !meetInfoPopup) {
setMeetInfoPopup(true);
}
}, [isAdmin, meetInfoPopup]); 

相关内容

  • 没有找到相关文章

最新更新