javascript ReactJS 组件中显示的视频未更新
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29291688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Video displayed in ReactJS component not updating
提问by Dana
I'm new to ReactJS (0.13.1), and I've created a component in my app to display HTML5 video.
我是 ReactJS (0.13.1) 的新手,我在我的应用程序中创建了一个组件来显示 HTML5 视频。
It seems to work perfectly but only for the first selection. The video that is actually displayed and playing in the page doesn't change when you switch from one video to another (when this.props.video
changes).
它似乎工作得很好,但仅适用于第一个选择。当您从一个视频切换到另一个视频时(this.props.video
更改时),页面中实际显示和播放的视频不会更改。
I can see the <source src='blah.mp4' />
elements update in the Chrome inspector but the actually rendered video in the page doesn't change and keeps playing if it was already. Same thing happens in Safari & Firefox. All the other elements update appropriately as well.
我可以<source src='blah.mp4' />
在 Chrome 检查器中看到元素更新,但页面中实际呈现的视频不会改变,如果已经改变,它会继续播放。同样的事情发生在 Safari 和 Firefox 中。所有其他元素也相应地更新。
Any ideas?
有任何想法吗?
Anyway my component below:
无论如何我的组件如下:
(function(){
var React = require('react');
var VideoView = React.createClass({
render: function(){
var video = this.props.video;
var title = video.title === ''? video.id : video.title;
var sourceNodes = video.media.map(function(media){
media = 'content/'+media;
return ( <source src={media} /> )
});
var downloadNodes = video.media.map(function(media){
var ext = media.split('.').slice(-1)[0].toUpperCase();
media = 'content/'+media;
return (<li><a className="greybutton" href={media}>{ext}</a></li>)
});
return (
<div className="video-container">
<video title={title} controls width="100%">
{sourceNodes}
</video>
<h3 className="video-title">{title}</h3>
<p>{video.description}</p>
<div className="linkbox">
<span>Downloads:</span>
<ul className="downloadlinks">
{downloadNodes}
</ul>
</div>
</div>
)
}
});
module.exports = VideoView;
})();
UPDATE:
更新:
To describe it another way:
用另一种方式描述它:
I have a list of links with onClick handlers that set the props of the component.
我有一个带有设置组件道具的 onClick 处理程序的链接列表。
When I click on a video link ("Video Foo") for the first time I get
当我第一次点击视频链接(“Video Foo”)时
<video title="Video Foo" controls>
<source src="video-foo.mp4"/>
<source src="video-foo.ogv"/>
</video>
and "Video Foo" appears and can be played.
并且出现“Video Foo”并且可以播放。
Then when I click on the next one ("Video Bar") the DOM updates and I get
然后当我点击下一个(“视频栏”)时,DOM 更新,我得到
<video title="Video Bar" controls>
<source src="video-bar.mp4"/>
<source src="video-bar.ogv"/>
</video>
However it is still "Video Foo" that is visible and can be played.
然而,它仍然是可见且可以播放的“Video Foo”。
It's like once the browser has loaded media for a <video>
it ignores any changes to the <source>
elements.
就像一旦浏览器为 a 加载了媒体,<video>
它就会忽略对<source>
元素的任何更改。
采纳答案by Dana
Found the answer
找到答案
Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect. To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources. Generally, manipulating source elements manually after the document has been parsed is an unnecessarily complicated approach
当元素已经插入到视频或音频元素中时,动态修改源元素及其属性将不起作用。要更改正在播放的内容,只需直接使用媒体元素上的 src 属性,可能会使用 canPlayType() 方法从可用资源中进行选择。通常,在解析文档后手动操作源元素是一种不必要的复杂方法
https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element
https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element
It's a pretty hacky and fragile, but it got the job done for my cases.
这是一个非常hacky和脆弱的,但它为我的案例完成了工作。
(function(){
var React = require('react');
var VideoView = React.createClass({
pickSource: function(media){
var vid = document.createElement('video');
var maybes = media.filter(function(media){
var ext = media.split('.').slice(-1)[0].toUpperCase();
return (vid.canPlayType('video/'+ext) === 'maybe');
});
var probablies = media.filter(function(media){
var ext = media.split('.').slice(-1)[0].toUpperCase();
return (vid.canPlayType('video/'+ext) === 'probably');
});
var source = '';
if (maybes.length > 0) { source = maybes[0]; }
if (probablies.length > 0) { source = probablies[0]; }
source = (source === '')? '' : 'content/'+source;
return source;
},
render: function(){
var video = this.props.video;
var title = video.title === ''? video.id : video.title;
var src = this.pickSource(video.media);
var downloadNodes = video.media.map(function(media){
var ext = media.split('.').slice(-1)[0].toUpperCase();
media = 'content/'+media;
return (
<li><a className="greybutton" href={media}>{ext}</a></li>
)
});
return (
<div className="video-container">
<video title={title} src={src} controls width="100%"></video>
<h3 className="video-title">{title}</h3>
<p>{video.description}</p>
<div className="linkbox">
<span>Downloads:</span>
<ul className="downloadlinks">
{downloadNodes}
</ul>
</div>
</div>
)
}
});
module.exports = VideoView;
})();
回答by evilguc
I have described some approaches for plain JavaScript here. Based on that I have found solutions for React which work for me:
我在这里描述了一些纯 JavaScript 的方法。基于此,我找到了适合我的 React 解决方案:
using
src
attribute on video itself:var Video = React.createComponent({ render() { return <video src={this.props.videoUrl} />; } });
Dana's answeris a great option extending this solution.
using
.load()
call on video element:var Video = React.createComponent({ componentDidUpdate(_prevProps, _prevState) { React.findDOMNode(this.refs.video).load(); // you can add logic to check if sources have been changed }, render() { return ( <video ref="video"> {this.props.sources.map(function (srcUrl, index) { return <source key={index} src={srcUrl} />; })} </video> ); } });
src
在视频本身上使用属性:var Video = React.createComponent({ render() { return <video src={this.props.videoUrl} />; } });
Dana 的回答是扩展此解决方案的绝佳选择。
使用
.load()
视频元素调用:var Video = React.createComponent({ componentDidUpdate(_prevProps, _prevState) { React.findDOMNode(this.refs.video).load(); // you can add logic to check if sources have been changed }, render() { return ( <video ref="video"> {this.props.sources.map(function (srcUrl, index) { return <source key={index} src={srcUrl} />; })} </video> ); } });
UPD:
更新:
of course it's possible to add unique
key
attribute for<video>
tag (for example based on your sources), so when sources will change it will be changed as well. But it will cause<video>
to be re-rendered completely and it may cause some UI flashes.var Video = React.createComponent({ render() { return ( <video key={this.props.videoId}> {this.props.sources.map(function (srcUrl, index) { return <source key={index} src={srcUrl} />; })} </video> ); } });
当然,可以
key
为<video>
标签添加唯一属性(例如,基于您的来源),因此当来源发生变化时,它也会发生变化。但它会导致<video>
完全重新渲染,并且可能会导致一些 UI 闪烁。var Video = React.createComponent({ render() { return ( <video key={this.props.videoId}> {this.props.sources.map(function (srcUrl, index) { return <source key={index} src={srcUrl} />; })} </video> ); } });
回答by Hossam Mourad
I faced the same issue and I didn't have access to the <video>
HTML tag as I was using a library to render the video (not the native <video>
HTML tag) which is internally responsible for rendering the <video>
tag.
我遇到了同样的问题,我无法访问<video>
HTML 标签,因为我使用库来呈现视频(不是本机<video>
HTML 标签),它在内部负责呈现<video>
标签。
In this case I have found another solution which I think is better to solve the same issue.
在这种情况下,我找到了另一种解决方案,我认为它可以更好地解决相同的问题。
Before:
前:
<VideoLibrary src={this.props.src} />
After:
后:
<React.Fragment key={this.props.src}>
<VideoLibrary src={this.props.src} />
</React.Fragment>
Or this if you're using the native <video>
HTML tag:
或者,如果您使用本机<video>
HTML 标记,则为:
<React.Fragment key={this.props.src}>
<video src={this.props.src} />
</React.Fragment>
This way React will render different video tags because the src
prop will be different hence rendering a different HTML tag each time to avoid this issue.
这样 React 将渲染不同的视频标签,因为src
prop 会不同,因此每次渲染不同的 HTML 标签以避免这个问题。
I find this way cleaner and simpler and will work in both cases if you have or don't have access to the <video>
HTML tag.
我发现这种方式更清晰、更简单,并且无论您是否有权访问<video>
HTML 标记,都可以在这两种情况下使用。
回答by MAHENDRA. A.R
Try this way
试试这个方法
import React, { Component } from "react";
class Video extends Component<any, any> {
video: any = React.createRef();
componentDidUpdate(preProps: any) {
const { url } = this.props;
if (preProps && preProps.url && url) {
if (preProps.url !== url) {
this.video.current.src = url;
}
}
}
render() {
const { url } = this.props;
return (
<video controls ref={this.video}>
<source src={url} type="video/mp4" />
Your browser does not support HTML5 video.
</video>
);
}
}
回答by yonizilberman
I had the same problem with making a playlist with videos.
我在制作带有视频的播放列表时遇到了同样的问题。
So I separated the video player to another react component and that component received two props: contentId (video identify) & videoUrl (video URL).
因此,我将视频播放器与另一个反应组件分开,该组件收到两个道具:contentId(视频标识)和 videoUrl(视频 URL)。
Also I added a ref to the video tag so I can manage the tag.
我还添加了对视频标签的引用,以便我可以管理标签。
var Video = React.createClass({
componentWillReceiveProps (nextProps) {
if (nextProps.contentId != this.props.contentId) {
this.refs['videoPlayer'].firstChild.src = this.props.videoUrl;
this.refs['videoPlayer'].load()
}
},
propType: {
contentId: React.PropTypes.string, // this is the id of the video so you can see if they equal, if not render the component with the new url
videoUrl: React.PropTypes.string, // the video url
} ,
getDefaultProps(){
return {
};
},
render() {
return (
<video ref="videoPlayer" id="video" width="752" height="423">
<source src={this.props.videoUrl} type="video/mp4" />
</video>
);
}
});
module.exports = Video;
this is much more clean:
这更干净:
<Video contentId="12" videoUrl="VIDEO_URL" />