YouTube Videos with React Native

React Native is a great framework to write apps for iOS and Android but sometimes React Native provides some really ugly things.

In my current project the client want to display his YouTube content. Nothing easier then that, right? Not really. On NPM is only one package that is suitable for the use case. It is: react-native-youtube (surprise! 🎉🎉). A great package but with some downsides. At the moment it seems that the package is dead but it is the only one that implements native features like: fullscreen, seeking, ... this and more want the client. So what is my problem?

On iOS it works great on Android ... kind of. On my Android emulator the player works great but on my real hardware device not. I have no controls so I can't play the video or do something else like seeking or fullscreen. I try to find my luck with a Google Search and ... yep it is a hack. To display the controls on Android devices you need to do the following:

class YTPlayer extends React.Component {
  state = {
    height: 215,
  };
  handleReady = () => {
    setTimeout(() => this.setState({ height: 216 }), 500);
  };
  render() {
    const { height } = this.state;
    const { id } = this.props;
    return <YouTube controls={1} videoId={id} onReady={this.handleReady} />;
  }
}

What this does is pretty simple. On component mount the player is rendered but if the video is ready we call the function: handleReady and update our state. Because we update our state we do a re-render and increase our height by one. This hacky way give us the controls. Great! But we have some more issues.

In my clients project I use react-navigation and I'm pretty happy with that. If I switch the screen from a video to something else and I switch back ... 💣 The app crashs because Android try to reload the player and resume some stuff but I have a fix! 🤓

Add resumePlayAndroid={false} to the player and this is gone. But wait if I do the same action again (switch the screens) I get an black screen or a video without controls. Damn this is not good. Thanks to react-navigation I can fix this. We use the withNavigationFocus function from react-navigation to determine if we focus the specific screen or not. If we, render the player. If we not, don't render the player.

Here is the complete component for you:

import React from "react";
import YouTube from "react-native-youtube";
import { withNavigationFocus } from "react-navigation";
import { Dimensions } from "react-native";
class YTPlayer extends React.Component {
  state = {
    height: (Dimensions.get("window").width / 16) * 9 + 1,
    width: "99.99%",
    key: Math.random(),
  };
  changeFullScreen = () => {
    setTimeout(
      () => this.setState({ width: "100%", height: this.state.height - 1 }),
      500
    );
  };
  render() {
    const { height, width, key } = this.state;
    const { id } = this.props;
    return (
      <YouTube
        key={key}
        controls={1}
        apiKey=""
        videoId={id}
        resumePlayAndroid={false}
        style={{
          alignSelf: "stretch",
          width: "100%",
          height: height,
        }}
        onReady={this.changeFullScreen}
        controls={1}
        fullscreen={false}
        onChangeState={this.changeFullScreen}
        onChangeFullscreen={this.changeFullScreen}
      />
    );
  }
}
const Player = ({ id, isFocused }) => {
  if (isFocused) return <YTPlayer id={id} />;
  return null;
};
export default withNavigationFocus(Player);