Daniel Silhavy
Daniel Silhavy

Reputation: 41

Reattaching DOM element in React component

I am facing a situation which requires me to completely remove an element from the DOM and reattach it. To be more specific, a bug in the Airplay functionality leads to missing track information when changing the src of a video element:

  1. Change the src attribute of the videoelement and start playing on Apple TV
  2. Now switch the source to a different video
  3. The track information (audio,text) are lost

I found a workaround which requires me to remove the video element from the DOM, create a new one and re-add it to the DOM. What would be the best way to do this with react? I tried setting the key attribute of my child component but this does not seem to do it. Here is an excerpt of the code:

class Container extends Component {
   render() {
    <Video key={this.videoElementKey} />
   }
} 

class Video extends Component {
  render() {
   return (
    <video id="vid" className="video-elem" playsInline />
  );

  componentDidMount() {
    console.log("componentDidMount");
   }
}

The component gets remounted like I would expect. Does this imply that the videoelement is removed and readded again? Moreover, is the ref property of the component refreshed?

Upvotes: 0

Views: 1014

Answers (2)

Saša Tomislav Mataić
Saša Tomislav Mataić

Reputation: 1636

What you can do, to force the element unmounting, is to create two elements which extend your Video element, and switch between them when you update the src. With conditional rendering, on each change, one of the video components will be unmounted, and another will be mounted in it's place.

Following is the example using a set array of video srcs:

import React, { Component } from 'react';

const videos = [
  'https://img-9gag-fun.9cache.com/photo/a9KrqE1_460sv.mp4',
  'https://img-9gag-fun.9cache.com/photo/a3Qq4PN_460sv.mp4',
  'https://img-9gag-fun.9cache.com/photo/aE2Yq9O_460sv.mp4'
];

class App extends Component {

  constructor(props) {
    super(props);
    this.onNext = this.onNext.bind(this);
    this.state = {
      current: 0
    }
  }

  onNext = event => {
     this.setState((prevState) => ({
       // using modulo to stay within the length of videos array
       current: (prevState.current + 1) % videos.length 
  }));
};

  render() {
    return (
      <div className="App">
          // switching between two video components if index is odd/even
        { this.state.current % 2 === 1
          ? <One current={this.state.current} />
          : <Two current={this.state.current} /> }
        <hr />
        <button onClick={this.onNext}>Next video</button>
      </div>
    );
  }
}

class Video extends Component {
  render() {
    return (
      <video key={'key-'+this.props.current} id="vid" className="video-elem" playsInline src={videos[this.props.current]}  />
    );
  }

  componentDidMount() {
    console.log("componentDidMount");
  }
  componentWillUnmount() {
    console.log("componentWillUnmount");
  }
}

class One extends Video {}

class Two extends Video {}

export default App;

Running example on repl.it.

Upvotes: 0

Edd
Edd

Reputation: 1032

I'm not sure of what you are trying to accomplish but, you may want to check unmountComponentAtNode . It allows you to actually unmount your custom component and the references to it, not only the HTML elements. Then, you can add your component again.

You can do something like:

ReactDOM.unmountComponentAtNode(document.getElementById(yourNodeID));

Sorry it's not a very complete answer but it's hard without knowing more of your implementation. Anyway, maybe it points you to the right direction.

Upvotes: 1

Related Questions