ReactJS не может менять видео и постеры

Я хочу изменить видео постер и источник VideoJS, когда выбрано видео изменилось

var VideoPlayer = React.createClass({
  render: function() {
    var selectedVideo = this.props.selectedVideo;
    function filterVideo(video) {
      return video.id == selectedVideo;
    }

    var data = this.props.videos.filter(filterVideo);
    return (
      <div className="col-md-6 videoplayer">
        <h2>{data[0].title}</h2>
        <video id="videoplayer" className="video-js vjs-default-skin vjs-big-play-centered" controls preload="none"
          width="100%"
          height="300"
          poster={data[0].poster}
          data-setup="{}">
          <source src={data[0].video} type='video/mp4' />
        </video>
        <div className="video-description">
          {data[0].description}
        </div>
      </div>
    );
  }
});

но я получил ошибку:

Uncaught Error: Invariant Violation: ReactMount: Two valid but unequal nodes with the same `data-reactid`: .0.0.1.1 

название и описание изменены, но видео постер не меняется

3 ответа

Решение

Это проблема с деструктивными библиотеками. В основном, что происходит, вы делаете <video> элемент, а затем VideoJS вставляет кучу элементов одного уровня рядом с вашим <video> (дочерние элементы в порядке).

React пытается обновить элемент, и он не может понять, что происходит, поэтому он паникует и выдает эту ошибку.

Итак, у вас есть два варианта:

Вариант 1: сделать <div ref="target" /> создайте видео узел в componentDidMount и this.refs.target.appendChild(that), а затем вызвать VideoJS вручную. В componentWillRecieveProps вам необходимо напрямую обновить src автора img.

Вариант 2: распакуйте video.js и сделайте так, чтобы он генерировал только события вместо прямой модификации DOM. Ваш компонент будет реагировать на эти события, изменяя состояние и отображая плакат, кнопки и т. Д.

Вариант 1 проще, вариант 2 потенциально более эффективен и чище. Я бы пошел с вариантом 1.


Ни одно из этих решений не является хорошим решением, но библиотеки, которые покидают свой контейнер, не очень хорошо реагируют на реакцию.

В случае, если кому-то нужен пример ответа от FakeRainBrigand, вот как выглядит мой класс видео в конце.

Video = React.createClass({
  componentDidMount: function() {
    var video, wrapper;
    wrapper = document.createElement('div');
    wrapper.innerHTML = "<video id='attachmentVideo' class='video-js vjs-default-skin' controls preload='auto' width='640' height='264' poster='" + this.props.thumbnail + "'><source src='" + this.props.url + "' type='video/mp4' /><p className='vjs-no-js'>To view this video please enable JavaScript, and consider upgrading to a web browser that <a href='http://videojs.com/html5-video-support/' target='_blank'>supports HTML5 video</a></p></video>";
    video = wrapper.firstChild;
    this.refs.target.getDOMNode().appendChild(video);
    return videojs(video, {});
  },
  render: function() {
    return (
      <div id="attachmentViewer">
        <h2>{this.props.title}</h2>
        <div id="attachmentVideoContainer" ref="target" />
      </div>
    );
  }
});

Есть вопрос, который отвечает на этот вопрос (я думаю, это должно сработать в вашем случае, дайте мне знать иначе). Вы можете изменить его на код React из их примера, который находится в jQuery, проверив, изменился ли игрок, а затем изменив src следующим образом:

componentDidUpdate(prevProps) {
    if (prevProps.src !== this.props.src) { //see if the src changes
        if (this.initialized) { //check if the player has been previously initialized
            this.player.src({ //set the players new source
                src: this.props.src,
                type: 'video/mp4'
            })
        } else { 
            this.attemptInitialization() //only initialize player if it hasn't been
        }
    }
}

1. импорт

import ReactDOM from 'react-dom'

2. внутренняя функция

this.setstate({vidio:"{new url as a source}") var name = ReactDOM.findDOMNode(this.video) name.src=this.state.video.url 

3. шаблон

<VideoPlayer ref={video => this.video = video}src={this.state.video.url}
                       />

Не на 100%, но если вы ищете более "чистое" решение для реагирования на реализацию videoJS как компонента, это то, что я сделал...

Последнее сообщение было некоторое время назад, и я не знаю, придумал ли кто-нибудь лучшую реализацию, однако, я сделал следующее:

  1. Добавил функцию videojs как реквизит getInitialState и также добавил реквизит "videoHTML" в getInitialState.
  2. Сгенерировал мой видео тег и источники в методе componentDidMount и назначил эту переменную строку для свойства videoHTML.
  3. В метод componentWillUnmount () добавлен dispose () созданного экземпляра проигрывателя vjs и replaceState из начального состояния.
  4. Добавлена ​​функция компонента __initVideo(elem), которая проверяет, есть ли у переданного элемента дочерние элементы, и если это так, то "this.vjsPlayer = this.state.initVideoJS(elem.children[0], {})". Затем я возвращаю this.vjsPlayer.
  5. В div вложения в методе render() я установил опору dangerouslySetInnerHTML в реквизит this.state.videoHTML и установил ссылку, которая вызывает функцию __initVideo, передавая в div.

Не могу опубликовать какой-либо код, но это настолько близко к чистой реализации React videoJS, что я смог выяснить.

Другие вопросы по тегам