【问题标题】:Reattaching DOM element in React component在 React 组件中重新附加 DOM 元素
【发布时间】:2018-12-03 16:39:56
【问题描述】:

我正面临需要从 DOM 中完全删除一个元素并重新附加它的情况。更具体地说,Airplay 功能中的一个错误会导致在更改视频元素的 src 时丢失曲目信息:

  1. 更改 videoelement 的 src 属性并开始在 Apple TV 上播放
  2. 现在将源切换到其他视频
  3. 曲目信息(音频、文本)丢失

我找到了一种解决方法,需要我从 DOM 中删除视频元素,创建一个新元素并将其重新添加到 DOM。用反应来做到这一点的最好方法是什么?我尝试设置我的子组件的 key 属性,但这似乎没有这样做。以下是代码的摘录:

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

组件按照我的预期重新安装。这是否意味着视频元素被删除并再次读取?另外,组件的ref属性是否刷新了?

【问题讨论】:

  • 是的,key 应该会有所帮助。通过您的示例,很难意识到有什么问题,因为&lt;Container&gt;&lt;Child&gt; 没有任何共同之处。在您的情况下,由于您需要在更改src 后重新安装&lt;video&gt; 我相信您可以使用key={this.props.urlToVideo} src={this.props.urlToVideo}
  • 只需将&lt;video&gt; 用作组件并通过更改道具来呈现它。进行跟踪会更容易。
  • 对不起Child的错误命名。我修复了这个问题,Video 组件现在在 Container 组件中被引用。但是,重新安装 Video 组件似乎不会从 DOM 中删除 HTML5 视频元素。由于上述 Airplay 实现中的错误,这就是我所需要的。在纯 JavaScript document.body.removeChild(document.getElementById('vid'));
  • 嘿@DanielSilhavy 在下面查看我的答案,它通过反应stackoverflow.com/questions/53598070/…从DOM 中删除组件

标签: javascript reactjs


【解决方案1】:

我不确定您要完成什么,但您可能需要检查 unmountComponentAtNode 。它允许您实际卸载自定义组件及其引用,而不仅仅是 HTML 元素。然后,您可以再次添加组件。

你可以这样做:

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

抱歉,这不是一个非常完整的答案,但如果不了解您的实施情况,这很难。无论如何,也许它会为您指明正确的方向。

【讨论】:

  • 感谢您的回答,仍然不确定这是否是我需要的。更准确地说。我需要能够真正从 DOM 中删除一个元素。我希望 React 在重新安装我的组件时执行此操作,但显然情况并非如此。我有点惊讶这是如此棘手。所以在我没有 React 的例子中,我只是这样做:document.body.removeChild(video); with video being document.getElementById("video");
  • 好吧,如果您想要从 DOM 中删除 HTML 元素节点),那么您需要执行您在上面评论中发布的操作。 React不会删除元素本身,因为它需要一些节点来(重新)安装组件......所以,是的,我之前的答案只有在你删除元素/节点并且由于某种原因附加到它的组件时才有用不会被删除(在这种情况下,即使你看不到它也会留在内存中)。
【解决方案2】:

要强制卸载元素,您可以创建两个扩展 Video 元素的元素,并在更新 src 时在它们之间切换。使用条件渲染,每次更改时,将卸载一个视频组件,并在其位置上安装另一个。

以下是使用一组视频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;

正在运行example on repl.it

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 2018-09-06
    • 1970-01-01
    • 2014-03-10
    相关资源
    最近更新 更多