【问题标题】:How to add React components dynamically without reload?如何在不重新加载的情况下动态添加 React 组件?
【发布时间】:2026-02-20 18:30:01
【问题描述】:

上下文

React 新手;我有一个父组件,它使用 react-youtube 组件从 1-6 个 youtube 视频中生成任何位置。想法是 YouTube 组件将根据数组中的 youtube 视频 ID 动态生成。目前,我正在生成像

这样的组件
{this.state.videoIds.map((videoId, index) =>
                    <YouTube
                        key={index}
                        opts={opts}
                        videoId={videoId}
                        onReady={this._onReady} />
                )}

就生成组件而言,它按预期工作。但是,问题是,当this.state.videoIds 得到更新时,它会导致所有YouTube 组件都被刷新。这种行为对我来说很有意义。

问题

我遇到的问题如下:如果功能保持不变,我需要一种能够跟踪每个 YouTube 组件播放器时间的方法,以便在发生刷新时,我可以将该数据传回刷新时一直到组件,以便能够从上次中断的地方开始播放。

所以我的问题是,有没有一种方法可以动态生成这些组件而无需刷新所有现有组件?

对于它的价值,我考虑过只使用一个父组件来为每个用例静态添加组件,但这看起来非常笨拙,我真的不想走那条路。

编辑

这感觉很重要,我根据在任何给定时间加载的玩家数量平均分配屏幕空间,代码如下所示:

calculateVideoWidth = () => {
        if (this.state.videoIds.length <= 3) {
            return (document.body.clientWidth - 15) / this.state.videoIds.length;
        } else {
            return (document.body.clientWidth - 15) / this.MAX_NUMBER_OF_COLUMNS;
        }
    };

    calculateVideoHeight = () => {
        if (this.state.videoIds.length <= 3) {
            return window.innerHeight - 4;
        } else {
            return (window.innerHeight - 4) / this.MAX_NUMBER_OF_ROWS;
        }
    };

在我的渲染方法中,我正在执行以下操作:

let opts = {
            height: this.calculateVideoHeight(),
            width: this.calculateVideoWidth()
        };

查看@Tholle 的评论后,我突然想到“重新加载”来自调整大小。我想这是有道理的。必须有更好的方法来做到这一点?

编辑 1

查看此链接以了解我的体验:Video App。在输入框中,只需添加一个以','分隔的视频ID。

【问题讨论】:

  • 如何将新 id 添加到 videoIds 数组中? It doesn't seem to be refreshing in my simple example,但我可能误会你了。
  • 只是抛出一个想法。将&lt;YouTube /&gt; 包装在另一个扩展 PureComponent 的组件中,以便仅在更新该组件的 videoId 时发生更新?
  • @Tholle,看起来我遇到的问题与布局有关。根据屏幕上的玩家数量,我将可见的屏幕空间平均分配给玩家。在我的渲染方法中,我有一个高度和宽度的函数调用。在问题中查看我的编辑。
  • 我不太了解你想要实现的布局,但它不能用 CSS 来实现,例如弹性盒?
  • 我认为更好的方法是删除硬编码的高度和宽度,并将 iframe 放入 div 容器中,并为它们设置适当的 n 列布局。在 google 上可以找到很多“响应式 YouTube iframe”教程,例如 google.dk/amp/s/benmarshall.me/responsive-iframes/amp

标签: javascript reactjs youtube-iframe-api


【解决方案1】:

此代码可能会帮助您从用户离开的任何位置开始播放视频。 问题是我们需要在用户离开的任何地方维护或存储最后播放的视频状态,其中一些在重新加载时永久不可擦除,因此这里使用localStorage 实现。

import React from "react";
import ReactDOM from "react-dom";
import YouTube from "react-`enter code here`youtube";

class App extends React.Component {
  state = {
    videoIds: ["2g811Eo7K8U"],
    videosPlayState: {}
  };

  componentDidMount() {
    let videosPlayState = localStorage.getItem("videosPlayState") || "{}";
    this.setState({
      videosPlayState: JSON.parse(videosPlayState)
    });
  }

  addVideo = () => {
    this.setState(prevState => ({
      videoIds: [...prevState.videoIds, "9kBACkguBR0"]
    }));
  };

  _onStateChange = (event, videoId) => {
    let newPlayState = { ...this.state.videosPlayState
    };
    newPlayState[videoId] = event.target.getCurrentTime();
    localStorage.setItem("videosPlayState", JSON.stringify(newPlayState));
  };

  _onReady(event, videoId) {
    event.target.seekTo(this.state.videosPlayState[videoId] || 0);
  }
  render() {
    const opts = {
      height: "200",
      width: "400",
      playerVars: {
        autoplay: 1
      }
    };
    return ( <
      div className = "App" >
      <
      div className = "video-container" > {
        this.state.videoIds.map((videoId, index) => ( <
          YouTube onStateChange = {
            event => {
              this._onStateChange(event, videoId);
            }
          }
          onReady = {
            event => {
              this._onReady(event, videoId);
            }
          }
          key = {
            index
          }
          videoId = {
            videoId
          }
          opts = {
            opts
          }
          className = {
            "item"
          }
          />
        ))
      } <
      /div> <
      div >
      <
      button onClick = {
        this.addVideo
      } > Add video < /button> <
      /div> <
      /div>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render( < App / > , rootElement);

这是css:

.video-container .item {
  padding: 25px;
  display: inline-block;
}
.video-container {
  display: flex;
  flex-wrap: wrap;
}

这里有一个链接可能对你有帮助 enter link description here

【讨论】:

    【解决方案2】:

    在多玩了 flexbox 之后,我能够解决这个问题。我最终得到以下结果:

    html, body, #root, #root > div, div.container, iframe {
        width: 100%;
        height: 100%;
        margin: 0;
    }
    
    div.container {
        display: flex;
        flex-wrap: wrap;
    }
    
    span {
        flex-basis: 33%;
        flex-grow: 1;
    }
    

    供参考,这是我的jsx

            <div>
                <div className="container">
                    {this.state.videoIds.map((videoId, index) =>
                        <YouTube
                            id="flex-item"
                            key={index}
                            videoId={videoId}
                            onStateChange={this._onStateChange}
                            onReady={this._onReady}/>
                    )}
                </div>
            </div>
    

    我认为这可以通过 flexbox 完成,但我还没有想出解决方案。我希望这对其他人有所帮助。

    【讨论】:

      最近更新 更多