diff --git a/src/players/YouTube.js b/src/players/YouTube.js
index 8498c617..f6f66398 100644
--- a/src/players/YouTube.js
+++ b/src/players/YouTube.js
@@ -20,6 +20,21 @@ export default class YouTube extends Component {
this.props.onMount && this.props.onMount(this)
}
+ componentWillUnmount () {
+ // The YouTube iframe API keeps a reference to every player it creates, so
+ // removing the container from the DOM is not enough to release the iframe.
+ // Without destroy() the iframe stays detached but reachable from window.YT,
+ // leaking the player and everything its event handlers close over.
+ if (this.player && typeof this.player.destroy === 'function') {
+ try {
+ this.player.destroy()
+ } catch (error) {
+ // destroy() throws if the iframe has already been removed
+ }
+ }
+ this.player = null
+ }
+
getID (url) {
if (!url || url instanceof Array || MATCH_PLAYLIST.test(url)) {
return null
diff --git a/test/players/YouTube.js b/test/players/YouTube.js
index f2c8c30f..90276b8b 100644
--- a/test/players/YouTube.js
+++ b/test/players/YouTube.js
@@ -78,6 +78,32 @@ test('load() when ready', t => {
getSDK.restore()
})
+test('componentWillUnmount() destroys the player', t => {
+ const destroy = sinon.fake()
+ const renderer = create()
+ const instance = renderer.getInstance()
+ instance.player = { destroy }
+ renderer.unmount()
+ t.ok(destroy.calledOnce, 'destroy() is called on unmount')
+ t.ok(instance.player === null, 'the player reference is released')
+})
+
+test('componentWillUnmount() without a player', t => {
+ const renderer = create()
+ renderer.unmount()
+ t.ok(true, 'unmounting before the player is ready does not throw')
+})
+
+test('componentWillUnmount() when destroy() throws', t => {
+ const renderer = create()
+ const instance = renderer.getInstance()
+ instance.player = {
+ destroy: () => { throw new Error('The YouTube player is not attached to the DOM') }
+ }
+ renderer.unmount()
+ t.ok(instance.player === null, 'a throwing destroy() does not break unmounting')
+})
+
test('onStateChange() - play', t => {
const called = {}
const onPlay = () => { called.onPlay = true }