Updating source URL on HTML5 video with React

24,476

Solution 1

Quick and dirty awnser: Add a key prop to <Clip> or <video>, e.g.:

function Clip({ url }) {
  return (
    <video key={url}>
      <source src={url} />
    </video>
  );
}

Recommended awnser: Trigger a load event for the video element whenever there's a new URL, e.g.:

function Clip({ url }) {
  const videoRef = useRef();
  const previousUrl = useRef(url);

  useEffect(() => {
    if (previousUrl.current === url) {
      return;
    }

    if (videoRef.current) {
      videoRef.current.load();
    }

    previousUrl.current = url;
  }, [url]);

  return (
    <video ref={videoRef}>
      <source src={url} />
    </video>
  );
}

Explanation: The video initially doesn't change because in essence you're only modifying the <source> element and React understands that <video> should remain unchanged, so it does not update it on the DOM and doesn't trigger a new load event for that source. A new load event should be triggered for <video>.

The dirty answer works because when you change the key of a React element, React understands that's a whole new element. It then creates a new element in the DOM which naturally will have its own load event triggered on mount.

Solution 2

Changing the src of <source /> doesn't seem to switch the video for some reason. This isn't a react issue I don't think. Probably just how <source /> works. Maybe you have to call .load() on the element again. But it's just easier to set it directly on the element itself.

See the comment to the top answer: Can I use javascript to dynamically change a video's source?

You can set src directly on element instead:

function Clip(props) {
  return (
    <video width="320" height="240" src={props.url} controls autoPlay>
    </video>
  )
}
Share:
24,476
Sherwood Callaway
Author by

Sherwood Callaway

I’m a software engineer with experience across the stack, currently doing CI/CD and Kubernetes at Crunchbase. I want to build software than makes a positive difference in the world. :)

Updated on August 25, 2022

Comments

  • Sherwood Callaway
    Sherwood Callaway over 1 year

    I want to update the source tag in a HTML5 video element so that when I click a button, whatever's playing switches to a new video.

    I have a Clip component that returns an HTML5 video element, with the source URL provided via props.

    function Clip(props) {
      return (
        <video width="320" height="240" controls autoPlay>
          <source src={props.url} />
        </video>
      )
    }
    

    I also have a Movie component, which contains multiple URLs, each corresponding to a section of the movie. It also contains a position attribute, which acts like an iterator by remembering which section is currently playing.

    class Movie extends Component {
      constructor() {
        super();
        this.state = {
          sections: [
            'https://my-bucket.s3.amazonaws.com/vid1.mp4',
            'https://my-bucket.s3.amazonaws.com/vid2.mp4'
          ],
          position: 0
        };
      }
      render() {
        const sections = this.state.sections
        const position = this.state.position
    
        return (
          <div>
            {position}
            <Clip url={sections[position]} />
            <button onClick={() => this.updatePosition()}>Next</button>
          </div>
        )
      }
      updatePosition() {
        const position = this.state.position + 1;
        this.setState({ position: position });
      }
    }
    

    The Movie component renders a Clip component and a "Next" button. When the Next button gets clicked, I want to update the position attribute and re-render the HTML5 video element using the next URL from sections.

    As of now, when I hit Next the HTML5 video source tag updates, but the element continues playing the video from the previous URL. Can anyone help me figure out how to reset the video element?

    Update: I created a JSFiddle, which you can see here.

    Update 2: Updating the src attribute on the video element works!