React+Zoom Web SDK视频应用如何保留活跃摄像头仅更新变动用户画面
问题根因
你当前的问题核心是CanvasRenderer组件定义在了Session组件内部,每次sessionUsers状态变更触发Session组件重渲染时,都会生成一个全新的CanvasRenderer组件实例,React会将其识别为新的组件类型,直接卸载旧的整个画布列表再重新挂载,导致所有已有的canvas元素被销毁,之前的视频渲染自然全部失效。
修复步骤
- 把
CanvasRenderer组件移到Session组件外部,保证组件类型稳定,让React.memo可以正常生效 - 自定义
React.memo的比较逻辑,避免不必要的重渲染 - 补充新用户加入时的视频状态检查,主动渲染已开启的视频
- 增加待渲染队列,处理canvas还未挂载就收到渲染请求的边界情况
修复后核心代码
// 移到Session组件外部定义,保证组件类型稳定 const areCanvasPropsEqual = (prevProps, nextProps) => { // 仅当用户列表的成员发生变化时才触发重渲染 const prevUserIds = prevProps.state.map(user => user.userId).sort().join() const nextUserIds = nextProps.state.map(user => user.userId).sort().join() return prevUserIds === nextUserIds } const CanvasRenderer = React.memo(({ state }) => { if (!state) return <></> return state.map((item) => ( <div className="canvas_container" key={item.userId}> <span className="canvas_name_container"> <p className="canvas_name_self">{item.userId || ''}</p> </span> <canvas className="canvas" data-canvas_id={item.userId}></canvas> </div> )) }, areCanvasPropsEqual) const Session = () => { // ... 其他原有状态 const stream = useRef({}) const [ sessionUsers, setSessionUsers ] = useState([]) // 新增待渲染队列,处理canvas还未挂载就收到渲染请求的情况 const pendingRenderTasks = useRef([]) useEffect(() => { // 组件更新后执行待处理的渲染任务 pendingRenderTasks.current.forEach(task => task()) pendingRenderTasks.current = [] }, [sessionUsers]) useEffect(() => { // ... 其他监听器 client.on('peer-video-state-change',async(payload) => { const { action, userId } = payload try { await participantsRender(action,userId) } catch (error) { console.log(error) } }) client.on('user-added', (user) => { const participants = client.getAllUser(); setSessionUsers(participants) // 新增:检查新用户是否已开启视频,主动触发渲染 if (user.video) { participantsRender('Start', user.userId) } }) client.on('user-removed', (user) => { const participants = client.getAllUser(); setSessionUsers(participants) }) },[client]) const participantsRender = async(action,userId) => { if (!action || !userId) return console.log('Empty Param') const canvas = document.querySelector(`.canvas[data-canvas_id="${userId}"]`) // 如果canvas还未渲染,加入待执行队列 if (!canvas) { pendingRenderTasks.current.push(() => participantsRender(action, userId)) return } if (action === 'Start'){ await stream.current.renderVideo(canvas, userId, 640, 360, 0, 0, 2); } else if (action === 'Stop') { await stream.current.stopRenderVideo(canvas, userId); } else { console.log('error') } } const toggleVideo = async() => { // 原有toggleVideo逻辑保持不变 if (!stream) return const uid = client.getCurrentUserInfo().userId const canvas = document.querySelector(`.canvas[data-canvas_id="${uid}"]`) if(!stream.current?.isCapturingVideo()) { try { await stream.current?.startVideo(); stream.current?.renderVideo(canvas, uid, 640, 360, 0, 0, 2); } catch (error) { console.log(error) } } else if (stream.current.isCapturingVideo()){ try { await stream.current.stopVideo() stream.current?.stopRenderVideo(canvas, uid) } catch (error) { console.log(error) } } } return ( <div> <div className="canvas_list_container"> <CanvasRenderer state={sessionUsers} /> </div> </div> ) }
修复后sessionUsers变化时,React只会新增/删除对应userId的canvas元素,已有的画布不会被卸载,已渲染的视频流就不会被中断。
内容的提问来源于stack exchange,提问作者Nuintee
相关产品推荐
相关产品推荐

