2

Here is the sandbox link: https://codesandbox.io/s/playvideo-e3dbw
I'm using video.js to display video in my react project, and it's imported in my <VideoPlayer/> component.
Now I want to use react-loadable to wrap <VideoPlayer/> cause video.js is too large.
But when I'm accessing the actural video player in my running project, the project was crashed and I got some error messages in my browser's console:

react-dom.development.js:57 Uncaught Invariant Violation: Element type is
invalid: expected a string (for built-in components) or a class/function (for
composite components) but got: object.

Check the render method of `LoadableComponent`.
Warning: Can't perform a React state update on an unmounted component. This is 
a no-op, but it indicates a memory leak in your application. To fix, cancel 
all subscriptions and asynchronous tasks in the componentWillUnmount method.

Here are some pieces of my source codes. The problem occurs after I wrapped <VideoPlayer/> component using react-loadable like so:

const LoadableVideoPlayer = Loadable({
  loader: () => import('./VideoPlayer'),
  render(loaded, props) {
    let Component = loaded.default;
    return <Component {...props}/>;
  },
  loading: <div>Loading...</div>
});

I want to render either pdf player or video player in a page according to current state. Here is the usage of <LoadableVideoPlayer/> component:

if (this.state.currentPlayer==='Video' && videoSrc) {
      return <LoadableVideoPlayer key={videoSrc} videoSrc={videoSrc}/>
    } else if (this.state.currentPlayer==='PDF' && pdfSrc) {
      return <PDFPlayer key={pdfSrc} pdfSrc={pdfSrc}/>
    }
}

This is my <VideoPlayer/> component:

import React from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

class VideoPlayer extends React.Component {
  componentDidMount() {
    const src = this.props.videoSrc;

    const videoPlayerOptions = {
      controls: true,
      sources:[
        {
          src:src,
          type:'video/mp4'
        }
      ]
    };

    this.player = videojs(this.videoNode, videoPlayerOptions, () => {
      console.log("Ready to play")
    })
  }

  componentWillUnmount() {
    // if (this.player) {
    //   this.player.dispose()
    // }
  }

  render() {
    return(
      <div data-vjs-player style={{width:'100%'}}>
        <video ref={node => this.videoNode = node} className="video-js" />
      </div>
    )
  }
}

export default VideoPlayer

Please notice that in componentWillUnmount() method I comment out those lines of code, since if not, I will get another error message even if I do not use react-loadable.
I think the problem might be here, but I searched a lot and didn't find any solutions.

For more details, here are where I import Loadable and use it.

import React from 'react';
import Loadable from 'react-loadable';
import PDFPlayer from './PDFPlayer';

const LoadableVideoPlayer = Loadable({
  loader: () => import('./VideoPlayer'),
  render(loaded, props) {
    let Component = loaded.default;
    return <Component {...props}/>;
  },
  loading: <div>Loading...</div>
});

class ResourcePlayer extends React.Component {
  state = {currentPlayer:'PDF'};

  onPlayerChange = selectedPlayer => {
    this.setState({currentPlayer:selectedPlayer})
  };

  renderPlayer = () => {
    const {videoSrc, pdfSrc} = this.props;

    if (this.state.currentPlayer==='Video' && videoSrc) {
      return <LoadableVideoPlayerkey={videoSrc} videoSrc={videoSrc}/>
    } else if (this.state.currentPlayer==='PDF' && pdfSrc) {
      return <PDFPlayer key={pdfSrc} pdfSrc={pdfSrc}/>
    } else {
      return (
        <div>
          {/*Some placeholder here, not important for this problem*/}
        </div>
      )
    }
  };

  render () {
    // suppose here are some logic to switch the state between 'PDF' and 'Video'
    return (
      {this.renderPlayer()}
    )
  }
}
Lee
  • 63
  • 6
  • Seeing the error, it seems there is issue with importing the component. Can you also post how are you exporting and importing `LoadableComponent`? – tarzen chugh Aug 08 '19 at 07:11
  • Sure, do you mean `LoadableVideoPlayer `? I just simply put it right above where it is used. I will post the code inside the problem main content. – Lee Aug 08 '19 at 10:59
  • Whichever component in which you are getting error for and it would be great if you could give [codesandbox link](https://codesandbox.io/s/new) for reproduction of issue. – tarzen chugh Aug 08 '19 at 11:09
  • Thanks for your advice. Here is the sanbox link: https://codesandbox.io/s/playvideo-e3dbw I don't know whether it can be accessed, since I'm not familiar with code sandbox – Lee Aug 08 '19 at 11:33
  • Use arrow function for Loading. [link](https://codesandbox.io/s/playvideo-sjj9n) – tarzen chugh Aug 08 '19 at 12:03

1 Answers1

0

I think I have found the reason. Instead of using <div>Loading...</div> in side that LoadableVideoPlayer component, I should use a react component for the loading property.

const LoadableComponent = () => {
   return <div>Loading...</div>
};

const LoadableVideoPlayer = Loadable({
  loader: () => import('./VideoPlayer'),
  render(loaded, props) {
    let Component = loaded.default;
    return <Component {...props}/>;
  },
  loading: LoadableComponent
});

Also, I missed some error messages and sorry for that. Here is the error messages that indicates this problem:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: <div />. Did you accidentally export a JSX literal instead of a component?
    in LoadableComponent (created by ResourcePlayer)
    in div (created by ResourcePlayer)
    in ResourcePlayer
Lee
  • 63
  • 6