Tim J
Tim J

Reputation: 1271

HTML Video tag not updating source on re render

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

Answers (2)

Shashwat Gupta
Shashwat Gupta

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

dance2die
dance2die

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>
  );
};

Check out the working demo.
Edit so.answer.57235013

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

Related Questions