React.js中同类组件如何实现播放视频时自动暂停其他组件播放
问题根源
两个Row组件各自内部维护独立的trailerUrl状态,状态完全隔离,一个Row触发播放时根本感知不到另一个Row的播放状态,自然没法做到自动停止其他行的视频。
实现方法
把播放状态上提到父组件AllRows做全局统一管理,给每个Row传入唯一标识、全局当前播放状态、状态更新回调即可实现互斥播放。
第一步:改造父组件AllRows
父组件存储全局播放状态,记录当前正在播放的视频所属的RowID和视频ID,把状态和更新方法透传给所有子Row:
import React, { useState } from "react"; import Row from "../Components/Row.js"; function AllRows() { // 全局播放状态结构:{ rowId: 视频所属行的唯一标识, videoId: YouTube视频ID } const [currentPlaying, setCurrentPlaying] = useState(null); return ( <div> <Row rowId="trending" fetchURL="/trending/all/week" // 替换成你自己Row对应的接口地址 currentPlaying={currentPlaying} onPlayChange={setCurrentPlaying} /> <Row rowId="topRated" fetchURL="/movie/top_rated" // 替换成你自己Row对应的接口地址 currentPlaying={currentPlaying} onPlayChange={setCurrentPlaying} /> </div> ); } export default AllRows;
第二步:改造子组件Row
删除组件内部自维护的trailerUrl状态,改为根据父组件传入的全局状态判断是否渲染播放器:
- 全局状态里的rowId和当前组件rowId匹配时,才渲染对应YouTube播放器
- 点击海报触发播放时,直接更新全局状态,其他Row检测到当前播放的不是自身内容,会直接卸载YouTube组件,视频自动停止
import React, { useState, useEffect } from "react"; import "../Styling/row.css"; import YouTube from "react-youtube"; import axios from "../Module/axios.js"; import movieTrailer from "movie-trailer"; // 补全你原代码缺失的导入 function Row({ rowId, fetchURL, currentPlaying, onPlayChange }) { const base_url = "https://image.tmdb.org/t/p/w500/"; const [movies, setMovies] = useState([]); // 仅当全局播放的视频属于当前行时,才取视频ID const trailerUrl = currentPlaying?.rowId === rowId ? currentPlaying.videoId : ""; useEffect(() => { async function fetchData() { const request = await axios.get(fetchURL); setMovies(request.data.results); return request; } fetchData(); }, [fetchURL]); const opts = { height: "390", width: "100%", playerVars: { autoplay: 1, }, }; const handleClick = (movie) => { if (trailerUrl) { // 点击当前行正在播放的视频,清空全局播放状态关闭播放器 onPlayChange(null); } else { movieTrailer(movie?.name || movie?.title || "") .then((url) => { const urlParams = new URLSearchParams(new URL(url).search); const videoId = urlParams.get("v"); // 更新全局播放状态为当前行的视频,其他行会自动卸载播放器 onPlayChange({ rowId, videoId }); }) .catch((error) => console.log(error)); } }; return ( <div> <div className="row__posters"> {movies.map((movie) => ( <img src={`${base_url}${movie.poster_path}`} // 补全你原代码没写全的海报路径 key={movie.id} alt={movie.title || movie.name} className="row__poster" onClick={() => handleClick(movie)} /> ))} </div> {trailerUrl && ( <YouTube className="row__video" videoId={trailerUrl} opts={opts} /> )} </div> ); } export default Row;
补充说明
你原代码里有几个小疏漏一并修正了:
- 缺失
movieTrailer方法的导入 - 海报src写的是占位文本,补全了和TMDB图片域名的拼接逻辑
- 原Row组件内部的
fetchURL没有定义,改为父组件传入,避免每个Row写死接口 - 这个方案不需要额外操作YouTube播放器实例,靠React组件的卸载逻辑就能直接停止视频播放,逻辑最简单也最稳定
内容的提问来源于stack exchange,提问作者Malik Hamza
相关产品推荐
相关产品推荐

