Tengo un componente React que usa el reproductor multimedia https://github.com/selz/plyr . Todo funciona bien hasta que el componente se desmonta, lo que genera un error de la API de Vimeo. Específicamente: Uncaught (in promise) TypeError: Cannot read property 'postMessage' of null
.
Después de que ocurre este error, intento cargar el módulo nuevamente, fallará debido a que this.player
no está undefined
, pero si lo destruye y vuelve a intentarlo, se cargará. ¿Quizás React Tree está guardando la primera iteración del componente y necesito destruirlo por completo de alguna manera?
Aquí está mi componente:
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\/[Az]+\/)|(groups\/[Az]+\/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; } }
Su problema está causado por la forma en que se destruye el objeto Plyr. Eche un vistazo al método _destroy()
, que es lo que se expone en la API de Plyr.
https://github.com/Selz/plyr/blob/master/src/js/plyr.js#L3234
Como puede ver, en el caso de Vimeo, esta línea plyr.embed.unload().then(cleanUp);
y la siguiente llamada a setTimeout
en claro que _destroy
no es una llamada síncrona.
Primero, plyr.embed.unload()
hace una llamada a esto:
https://github.com/vimeo/player.js/blob/e0f607196f7b38e3a9891e70dda38da2731cff79/src/player.js#L440
que devuelve una Promesa . Una vez que se cumple la promesa, se ejecuta la función de cleanup
en Plyr, y ahí es cuando se actualiza el DOM y se restablecen las variables. Lo que esto significa es que su componente se desmontará sin esperar a que la instancia de Plyr se restaure por completo a los valores predeterminados o se "destruya". Esto puede provocar un comportamiento inesperado si vuelve a montar el componente de inmediato.
Dentro de componentWillUnmount
, le sugiero que no confíe en destroy()
para actualizar el DOM y restablecer la instancia de Plyr. En su lugar, puede eliminar el elemento DOM que envuelve la inserción de video y simplemente configurar this.player
en null
para que la instancia de Plyr interna sea destruida y recopilada por el gc. La próxima vez que se monte su componente, los elementos DOM que contenían el video no estarán allí y su reproductor se inicializará normalmente, configurando una nueva instancia de Plyr y creando el html necesario nuevamente.
Puede usar la API de bajo nivel de ReactTransitionGroup para obtener acceso a un nuevo ciclo de vida, componentWillLeave
.
npm i -S react-addons-transition-group
Le permitirá esperar a que el reproductor haya sido destruido antes de desmontar su componente, llamando a la devolución de llamada proporcionada. Un tiempo de espera de 200 ms debería ser suficiente, pero puede aumentarlo si quiere estar seguro.
Así que deshazte de tu componentWillUnmount
y reemplázalo por algo como
componentWillLeave(cb) { this.player[0].destroy(); setTimeout(cb, 200); }
Tendrá que envolver su componente de Vimeo
en un ReactTransitionGroup
, pero, por supuesto, puede crear un envoltorio si no le gusta y se reutilizará en otro lugar.
Además, es posible que desee ver react-plyr . Tiene el mismo problema que usted y es bastante nuevo, pero tiene pruebas, una estructura agradable y admite más opciones. Podría ser una buena idea unir esfuerzos para convertirla en una biblioteca genial si tienes tiempo;)