3 min read 0 comments

For our capstone project at Fullstack Academy, my group created an app we called “MyFlix” that is a clone of Plex, a media server app. We used Electron and React to create the app. I have never heard of Plex before we started the project but I learned a lot not only on how to use Electron and use React in it, but also on how it is to work in a group.

One task I was assigned was to create the movie player for the app. The requirements for this component are that it should play a movie file and it should have a custom video control. Rendering the movie that can play was easy, thanks to a node package called ReactPlayer. Creating the custom video control that is able to play, pause, forward and back the video as well as increase and decrease the volume of the video was a little challenging. (ReactPlayer has a prop called controls, the default video control for the ReactPlayer component, but we wanted a custom one.)

This was due to the fact that I have to take over the functions for these activities (play, pause, forward, back, increase volume, decrease volume), but also I have to make sure that the control appear during user activity and disappear otherwise. For this post, I will talk about how I made the video control element or component do this.

Looking back, it would have been easier if I first created a code that could do this:

In the video, the element I should hide only shows when the mouse moves and hides again when there is no activity. Also, is should be visible upon page loading, then disappears if there is no user activity (the video doesn’t clearly show that but at ~0:03, the page was reloaded). That is basically what most video controls do. (For example, check the YouTube video above.)

Eventually, when I couldn’t do this in the actual React component I was doing this in, I went ahead and created a code that does what was happening above with some help from my bootcamp instructor.

This involved putting a shouldHide state on the component. This state should change from false to true after a few seconds after page loading. To do this, we used a setTimeout function (assigned to a variable called timerId which was set to null in the constructor function) that calls a setState to change shouldHide. We set the timeout to two seconds. We defined timerId in a function called hidingTimer.

This function was then used in the componentDidMount lifecycle hook to start the timer upon page load, and when the timer ends, shouldHide is changed to true.

class Hiding extends React.Component {
    constructor() {
        super();
        this.state = {
            shouldHide: false
        };
        this.timerId = null;
    }

    hidingTimer() {
        this.timerId = setTimeout(() => {
            this.setState({ shouldHide: true });
        }, 2000)
    }

    componentDidMount() {
        this.hidingTimer()
    }
    // ...
}

Another function we created was handleMouseMove which changes shouldHide to false when the mouse moves, and starts the timer again so shouldHide would change to true when movement wanes.

    handleMouseMove() {
        clearTimeout(this.timerId); // clear the setTimeout first
        this.setState({ shouldHide: false });
        this.hidingTimer();
    }

Note that the two functions, handleMouseMove and hidingTimer were bound to this in the constructor function to make it work for the Hiding component.

Finally, in the render function, we take advantage of conditionals to change the class name of the div that contains the element that should hide. The final code for the React component looks like this:

import React from "react";

class Hiding extends React.Component {
  constructor() {
    super();
    this.state = {
      shouldHide: false
    };
    this.timerId = null;
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.hidingTimer = this.hidingTimer.bind(this);
  }

  hidingTimer() {
    this.timerId = setTimeout(() => {
      this.setState({ shouldHide: true });
    }, 2000);
  }

  componentDidMount() {
    this.hidingTimer();
  }

  handleMouseMove() {
    clearTimeout(this.timerId);
    this.setState({
      shouldHide: false
    });
    this.hidingTimer();
  }

  render() {
    return (
      <div>
        <div onMouseMove={this.handleMouseMove}>
          <h1>Move here</h1>
          <div className={this.state.shouldHide ? "hidden" : "none"}>
            <h1>I should hide</h1>
          </div>
        </div>
      </div>
    );
  }
}

This code for the component makes possible the desired characteristics of the component shown in the video above.

So, I went ahead and applied this code to the actual video control component I was working on. However, I encountered another problem, which was the occurence of memory leaks. Apparently, this is common with setTimeout functions in a React app. The recommended fix was to include a componentWillUnmount function that clears the setTimeout function. Luckilly, this worked for us, and the video control does what is supposed to do without the memory leaks.

componentWillUnmount() {
    clearTimeout(this.timerId)
}

I wonder, however, why this doesn’t occur in the React app I created above.