Reputation: 1271
I have a react component that renders 2 <video>
elements.
At any time only 1 element is visible and there is some logic that updates the state so these toggles.
I have an array of ids and on each render of the state/player toggle, I would like to update the playing video with a random video.
(I have added a button to toggle the state for the sake of this question)
If I log out my state in the render method I can see the url
prop is updated as expected, however the video does not change source.
I am choosing to toggle the visibility
of the element as display:none
or something like
{playerOne.visible &&
<LandingPageVideoPlayer url={playerOne.url} />
}
causes a flashing when the state is re-rendered.
I can't work out how to change the source of the video element on re-render.
Components
const LandingPageVideoPlayer = ({ url, isActive }) => {
const playerClassName = `video-player ${isActive ? 'video-player--is-active' : null}`
return (
<video className={playerClassName} autoPlay loop muted>
<source src={url} type='video/mp4' />
</video>
)
}
class LandingPage extends Component {
MOCK_URL_SOURCE = [
'ehZqNokVylyWk',
'hDqq4LalRAUiQ',
'yjTccXlnh6LXW',
'3FjEPbKqEPhPpmC8uY',
'3ohs7NLUXtNW98mtIQ'
]
state = {
playerOne: {
visible: true,
url: `https://media0.giphy.com/media/3ohs7NLUXtNW98mtIQ/giphy.mp4`
},
playerTwo: {
visible: false,
url: `https://media0.giphy.com/media/3FjEPbKqEPhPpmC8uY/giphy.mp4`
}
}
randomItem = () => {
return this.MOCK_URL_SOURCE[Math.floor(Math.random() * this.MOCK_URL_SOURCE.length)]
}
toggle = () => {
this.setState(prevState => ({
playerOne: { visible: !prevState.playerOne.visible, url: `https://media0.giphy.com/media/${this.randomItem()}/giphy.mp4` },
playerTwo: { visible: !prevState.playerTwo.visible, url: `https://media0.giphy.com/media/${this.randomItem()}/giphy.mp4` }
}))
}
render() {
const { playerOne, playerTwo } = this.state
if (!playerOne || !playerTwo) {
return null
}
return (
<div className='landing-page'>
<button onClick={this.toggle}></button>
<LandingPageVideoPlayer url={playerOne.url} isActive={playerOne.visible} />
<LandingPageVideoPlayer url={playerTwo.url} isActive={playerTwo.visible} />
</div>
)
}
}
export default LandingPage
Video Player SCSS
.video-player {
position: absolute;
object-fit: cover;
width: 100%;
height: 100vh;
visibility: hidden;
&--is-active {
visibility: visible;
}
}
Upvotes: 5
Views: 4370
Reputation: 5264
add key={this.state.url} like this
<div className="container">
<div className="row">
<div className="col-8">
<video width="720" key={this.state.url} height="540" controls >
<source src={this.state.url} type="video/mp4" />
</video>
<h1>{this.state.url}</h1>
Upvotes: 1
Reputation: 36905
React doesn't know which video to update unless key
property for each video is provided.
So when you set the key prop for the video to something unique, say url
pass to the player, it should work.
const LandingPageVideoPlayer = ({ url, isActive }) => {
const playerClassName = `video-player ${
isActive ? "video-player--is-active" : null
}`;
return (
..... 👇 .....
<video key={url} className={playerClassName} autoplay loop muted>
<source src={url} type="video/mp4" />
</video>
);
};
For more information how the key property works, check out the official documentation List and Keys.
The first sentence reads,
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity
In your case, you have two videos (which is actually a list of two items), so to help React which video has changed, key
would help React to identify which video has changed.
Upvotes: 15