Search code examples
next.jsserver-side-renderingdynamic-importreact-player

Dynamic component imported stuck in loading phase : next js


I am new to next js. In my project I need to display youtube videos. I have an api which provides me the video ids to show with its meta details. I wanted to create dynamic pages for each videos. I am using react-player as player.

Here is my code

[videoId].tsx

import Head from 'next/head';
import { useRouter } from 'next/router'
import Layout from '../../components/layout';
import { IVideoItem } from '../../models/videos.model';
import VideoContainer from '../../components/videos-page/video-container';
import { getVideosPaths, getVideosPageTitle, getVideosPageDescription, getVideosData  } from '../../services/videos-page.services';

export default function VideoPage({videoInfo} :IVideosPageProp) {
    const router = useRouter()

  if (router.isFallback) {
    return <div>Loading...</div>
  }
  return(
    <>
        <Head>
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <meta charSet="utf-8" />
            <title>{getVideosPageTitle(videoInfo)}</title>
            <meta name="description" content={getVideosPageDescription(videoInfo)} />
            <meta property="og:title" content={getVideosPageTitle(videoInfo)} key="ogtitle" />
            <meta property="og:description" content={getVideosPageDescription(videoInfo)} key="ogdesc" />

        </Head>
        <VideoContainer data={videoInfo} />
    </>
  )
}


export async function getStaticPaths() {
    const paths = await getVideosPaths()
    //console.log('paths: ',paths);
    return {
      paths,
      fallback: false
    }
  }

  export async function getStaticProps({ params }:IVideosPageStaticProp) {
    const {videoId} = params;
    const videoInfo = await getVideosData(videoId)
    return {
      props: {
        videoInfo
      }
    }
  }

interface IVideosPageProp {
    videoInfo: IVideoItem
}

interface IVideosPageStaticPropParams {
    videoId: string
}

interface IVideosPageStaticProp {
    params: IVideosPageStaticPropParams
}

video-container.tsx

import { Row, Col } from 'react-bootstrap'
import { IVideoItem } from '../../models/videos.model';
import styles from './videos-container.module.scss';
import VideoTag from '../home/videos-block/video-tag';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEye, faThumbsUp, faThumbsDown } from '@fortawesome/free-solid-svg-icons';
import moment from 'moment';
import dynamic from 'next/dynamic';

const ReactPlayer = dynamic(
    () => import('react-player'),
    { loading: () => <p>...</p>, ssr: false }
)
  

export default function VideoContainer({data} :IVideosPageProp){
    const videoInfo:IVideoItem = data;
    const videoTag = [{"tagName": "Foo", "tagId": 1}]
    const fallBackElement = () => {
        return <img src={videoInfo.default_thumbnail_url} width="100%"/>
    }

    return (
        <div className={styles['videos-container']}>
            <ReactPlayer
                url={`https://youtu.be/${data.video_id}`}
                controls
                width = "100%"
                light={true}
                playing={true}
                fallback={fallBackElement()}
                config={{
                    youtube: {
                      playerVars: { showinfo: 1 }
                    }
                }}
            />
            <div className={styles['videos-body']}>
                <div className={styles['tag-list-container']}>
                    {videoTag.map((tag, index) =>{
                        return <VideoTag videoTag={tag} key={index}/>
                    })}
                </div>
                <div className={styles['video-title']}>
                    {videoInfo.title}
                </div>
                <Row className={styles['video-numbers']}>
                    <Col md={2} xs={2}><FontAwesomeIcon icon={faEye} className={styles['views-icon']} />{videoInfo.views_count}</Col>
                    <Col md={2} xs={4}>{moment(new Date(videoInfo.published_at)).format('Do MMMM YYYY')}</Col>
                    <Col md={4} xs={2}></Col>
                    <Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsUp} className={styles['views-icon']} />{videoInfo.like_count}</Col>
                    <Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsDown} className={styles['views-icon']} />{videoInfo.dislike_count}</Col>
                </Row>
                <div className={styles['video-description']}>
                    {videoInfo.description}
                </div>
                
            </div>
        </div>
    )
}

interface IVideosPageProp {
    data:IVideoItem
}

When I run yarn dev the page is loading properly and the video player is rendering and working as expected. But when I run next buld and after that next start, the page is loading, but player is not loading. Insted it shows the "Loading..." message on the page, I refreshed several times, no luck. Not able to understand the issue. Can any one help?

Update 1: The page is rendering with video title, video description etc. But the dynamically imported video player is not rendered. At the place of video player, it shows 'Loading...'.


Solution

  • Not sure if you can dynamically load from the node_module, like this:

    const ReactPlayer = dynamic(
        () => import('react-player'),
        { loading: () => <p>...</p>, ssr: false }
    )
    

    But you should be able to do this by creating a react-player component first, then dynamic import it like this:

    // create a component named Player.js
    import ReactPlayer from 'react-player';
    
    const Player = props => (<ReactPlayer {...props}/>)
    export default Player;
    
    // then dynamic import it:
    const Player = dynamic(
        () => import('../components/Player'),
        { ssr: false }
      )
    
    // Then use <Player> with the same props