Reputation: 41
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:
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
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 src
s:
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
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