2017-01-04 89 views
3

我有一個使用https://github.com/selz/plyr媒體播放器的React組件。一切正常,直到組件解除安裝,這會導致Vimeo API發生錯誤。具體來說:Uncaught (in promise) TypeError: Cannot read property 'postMessage' of nullVimeo API與ReactJS和Plyr

發生此錯誤後,我嘗試再次加載模塊,它將因this.playerundefined而失敗,但是如果您銷燬它並再次嘗試,將會加載該模塊。也許React樹正在保存組件的第一個迭代,我需要以某種方式徹底銷燬它?

這裏是我的組件:

import React, {Component, PropTypes} from 'react'; 
import {findDOMNode} from 'react-dom'; 
import plyr from 'plyr'; 

/** 
* @desc Regex to retrieve the Vimeo video ID from the URL. 
* @type {RegExp} 
*/ 
const regex = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/g; 

export default class Vimeo extends Component { 

    static propTypes = { 
    url: PropTypes.string.isRequired, 
    }; 

    constructor(props) { 
    super(props); 
    // Use regex to get the video id from the url 
    this.videoId = regex.exec(this.props.url); 
    } 

    /* 
    |-------------------------------------------------------------------------- 
    | Digest Cycle 
    |-------------------------------------------------------------------------- 
    */ 

    /** 
    * @desc Initiate video player. 
    */ 
    componentDidMount() { 
    this.player = plyr.setup(findDOMNode(this), { 
     controls: [], 
     autoplay: true, 
     loop: true, 
     mute: true, 
    }); 
    this.player[0].on('ready',() => { 
     // Mute the video 
     if (!this.player[0].isMuted()) { 
     this.player[0].toggleMute(); 
     } 
    }); 
    } 

    /** 
    * @desc Destroy video player 
    */ 
    componentWillUnmount() { 
    this.player[0].destroy(); 
    } 

    /* 
    |-------------------------------------------------------------------------- 
    | Render 
    |-------------------------------------------------------------------------- 
    */ 

    render() { 
    let player = null; 
    if (typeof this.videoId !== 'undefined' && this.videoId !== null) { 
     player = (
     <div> 
      <div 
      data-type="vimeo" 
      data-video-id={this.videoId} 
      /> 
     </div> 
    ); 
    } 

    return player; 
    } 
} 

回答

1

你的問題是由其中Plyr對象被銷燬的方式造成。看一看_destroy()的方法,這是在Plyr API中暴露出來的。

https://github.com/Selz/plyr/blob/master/src/js/plyr.js#L3234

正如你可以看到,在Vimeo的情況下,該行plyr.embed.unload().then(cleanUp);及以下調用setTimeout說清楚,_destroy不是同步調用。

首先,plyr.embed.unload()撥打電話時這樣的:

https://github.com/vimeo/player.js/blob/e0f607196f7b38e3a9891e70dda38da2731cff79/src/player.js#L440

它返回一個無極。一旦履行承諾,Plyr中的cleanup函數就會被執行,那就是當DOM被更新並且變量被重置時。這意味着您的組件將在不等待Plyr實例完全恢復爲默認或「銷燬」的情況下卸載。如果您立即重新安裝組件,這可能會導致意外的行爲。

Inside componentWillUnmount,我建議你不要依靠destroy()來更新DOM並重置Plyr實例。您可以刪除包含視頻嵌入的DOM元素,並將this.player設置爲null,這樣Plyr實例就會被gc捕獲並收集。下一次您的組件被掛載時,持有視頻的DOM元素將不會在那裏,您的播放器將正常初始化,設置一個新的Plyr實例並再次創建必要的html。

1

您可以使用ReactTransitionGroup低級API訪問新的生命週期componentWillLeave

npm i -S react-addons-transition-group 

它將允許您通過調用提供的回調來等待播放器在卸載組件之前被銷燬。 200ms的超時應該足夠了,但是如果你想要安全,你可以增加它。

所以擺脫你componentWillUnmount,並通過類似

componentWillLeave(cb) { 
    this.player[0].destroy(); 
    setTimeout(cb, 200); 
} 

你必須要包裝你Vimeo成分爲ReactTransitionGroup,但你當然可以創建一個包裝,如果你不喜歡更換它會被其他地方重用。


此外,你可能想看看react-plyr。它和你有相同的問題,並且是相當新的,但它有測試,結構不錯,支持更多選項。如果你有時間,可以加入努力使其成爲一個很酷的庫;)

+0

@RyanPotter任何更新? :) –