-1

I’m trying to make a react video player. Video gets loaded fine, when I press play, it gets played and so on. Trouble occurs when I want to display duration of it. I tried accessing it via ref. It gets video, but ref returns null? I’m console logging and in it, amongst other properties is duration. Here’s an image of: enter image description here

To add: I’m calling ref in componentDidMount() just to see if it’s being ‘catched’. Here is the code:

import React from 'react';
import ReactDOM from 'react-dom';

class Show extends React.Component {

    constructor(props) {

        super(props);
        this.state = {
            video: "",
            switch: false,
            remaining: null,
        };
        this.playPause = this.playPause.bind(this);
        this.videoRef = React.createRef();
        this.trackTime = this.trackTime.bind(this);
        this.volume = this.volume.bind(this);
        this.fullScreen = this.fullScreen.bind(this);

    }

    fullScreen(){

        let elem = this.videoRef.current;
        if (elem.requestFullscreen){
            elem.requestFullscreen();
          } 
          else if (elem.mozRequestFullScreen){ /* Firefox */
            elem.mozRequestFullScreen();
          } 
          else if (elem.webkitRequestFullscreen){ /* Chrome, Safari & Opera */
            elem.webkitRequestFullscreen();
          } 
          else if (elem.msRequestFullscreen){ /* IE/Edge */
            elem.msRequestFullscreen();
          }

    }

    volume(e){
        this.videoRef.current.volume = e.target.value/100;
    }

    trackTime(e){
        this.setState({
            remaining: e.target.duration - e.target.currentTime,
        });
    }

    playPause(){

        this.setState({
            switch: !this.state.switch
        });

        if(this.videoRef.current.paused){
            this.videoRef.current.play();
        }
        else{
            this.videoRef.current.pause();
        }

    }

    componentDidMount(){

        let token = document.querySelector("meta[name='csrf-token']").getAttribute("content");

        let urlId = window.location.href;
        let getaVideoIdId = urlId.lastIndexOf("/");
        let videoId = urlId.substring(getaVideoIdId+1, urlId.length);

        $.ajax({
            url: '/showAjax',
            type: 'POST',
            data: {_token: token , message: "bravo", videoId: videoId},
            dataType: 'JSON',
            success: (response) => { 
                console.log("success");
                //console.log(response);
                this.setState({
                    video: response.video,
                }); 

            },
            error: (response) => {
                console.log("error");
                console.log(response);
            }
        }); 
        console.log(this.videoRef);
        //this.videoRef.current.play();
    }

    render(){
        //console.log(this.state);

        let PlayPause = this.state.switch ? "fa fa-pause-circle" : "fa fa-play-circle";

        let minutes = Math.floor(this.state.remaining/60);
        minutes = (""+minutes).length===1 ? "0"+minutes : minutes;//Checks if mins are one digit by turning it into string that now beasues length, if length is 1(single digit), if it is, then adds zero in front of it.
        let seconds = Math.floor(this.state.remaining%60);
        seconds = (""+seconds).length===1 ? "0"+seconds : seconds;//Same as mins, but for seconds.
        let remainingTime = minutes+" : "+seconds;

        let videoUrl = this.state.video && this.state.video.name ? "/storage/"+this.state.video.user.name+"'s Videos/"+this.state.video.name : null;

        let video = videoUrl ? <div  className={"videoWrapper"}><video ref={this.videoRef} preload="auto" onTimeUpdate={this.trackTime}>
            <source src={videoUrl} type="video/mp4"/>
            <source src={videoUrl} type="video/ogg"/>
            Your browser does not support the video tag.
            </video>
            <div id="controls">

                <button className="btnV" onClick={this.playPause}><i className={PlayPause}></i></button>
                <div className="time">{remainingTime}</div>
                <input type="range" className="custom-range" id="customRange" name="points1" onChange={this.volume}/>
                <div className="time" onClick={this.fullScreen}><i className="fa fa-expand"></i></div>

            </div>
        </div> : "";

        return (
            <div className="container">
                {video}
            </div>
        );

    }

}

if(document.getElementById('show')){

    ReactDOM.render(<Show/>, document.getElementById('show'));

}

Edit1: Also autoPlay don't always work when i refresh page ... And shows "Uncaught (in promise) DOMException".

  • 1
    Possible duplicate of [Is Chrome's JavaScript console lazy about evaluating arrays?](https://stackoverflow.com/questions/4057440/is-chromes-javascript-console-lazy-about-evaluating-arrays) (DOM node lists are array-like objects) – JJJ Sep 21 '19 at 19:26

1 Answers1

1

componentDidMount() is invoked immediately after a component is mounted.

Before calling componentDidMount(), render() will be called to init DOM.

But, in render() you did like below:

let videoUrl = this.state.video
   && this.state.video.name ? "/storage/"+this.state.video.user.name+"'s Videos/"+this.state.video.name : null;

let video = videoUrl ? <div  className={"videoWrapper"}><video ref={this.videoRef} preload="auto" onTimeUpdate={this.trackTime}>

At that first render(), videoUrl may be false because state.video is not initialized yet.

That means this.videoRef won't be initialized in this render funtion.

So it is null yet.

Then it is componentDidMount() 's turn to run.

At componentDidMount() you fetched video and inited state.video.

So then the next render() will be called. And also this.videoRef will be initialized, but not in componentDidMount().

That's why your console.log showed null.

this.videoRef will be initialized like below steps.

  1. first render function called.

    this.state.video -> ""

    this.videoRef -> null

  2. componentDidMount called

    this.state.video -> something valid.

    this.videoRef -> null

  3. another render function as updating this.state.video called

    this.videoRef -> something valid.

EDIT: You can access valid this.videoRef when it is initialized at the first time in componentDidUpdate().

componentDidUpdate(prevProps, prevState) {
  if(prevState.video !== this.state.video) { // this.state.video is updated
     // That is the place where you can access into valid this.videoRef
  }
}
Diamond
  • 3,470
  • 2
  • 19
  • 39
  • Long story short: Is there a way to get duration and play video unless i play video onClick. And onClick it works. So how to do that? – Veljko Stefanovic Sep 21 '19 at 20:56
  • Similar behavior as before. Sometimes work some things. For example if i do `this.videoRef.current.play();`, it will be executed, sometimes it will be not. On the other hand, `this.videoRef.current.duration` will always show NaN. Error: "Uncaught (in promise) DOMException". – Veljko Stefanovic Sep 21 '19 at 21:29
  • Did you call them in `componentDidUpdate()`? – Diamond Sep 21 '19 at 21:41
  • Yes. I also tried ```componentDidUpdate(prevProps, prevState){ if(prevState.video === this.state.video){ console.log(this.videoRef.current.duration); } }``` Which is only activated when i click on play button ... Which isn't very helpful considering that i need duration beforehand. – Veljko Stefanovic Sep 21 '19 at 22:08
  • @VeljkoStefanovic It is not `===`, it's `!==`. – Diamond Sep 21 '19 at 22:18
  • If you notice [quote] "I also tried" [/quote] . Emphasize on "also" which means that i tried original example first, then this. – Veljko Stefanovic Sep 21 '19 at 22:21