1

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...'.

  • You need to return `fallback: true` from `getStaticPaths`, to display a fallback page and have it updated when data is available. – PsyGik Jun 19 '21 at 10:42
  • @PsyGik, Ok, Thanks. But that doesn't solve my problem. Page is loading for me, except video, title, description everything coming correctly. – SANGEETH KUMAR S G Jun 19 '21 at 13:33
  • Your question mentions that it shows "Loading..." message on the page. As per your code, "Loading..." is a fallback state and not the actual page. So the question here is, is the page loading in fallback mode, or the actual content is rendered? – PsyGik Jun 19 '21 at 13:55
  • @PsyGik, Page is rendering, but in the place of video player, it shows 'Loading..'. So the dynamically imported react player not rendered. Rest of the contents are rendered and displaying. – SANGEETH KUMAR S G Jun 19 '21 at 15:15

1 Answers1

1

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
PsyGik
  • 3,535
  • 1
  • 27
  • 44
  • I tried your approach. But same result. Player is not rendering. It shows the 3 dots(...), at the place of the player. Dev server working fine, but when I run the page using next start after build, it is not working. – SANGEETH KUMAR S G Jun 21 '21 at 06:41
  • Can you post some steps on how to reproduce this? I created a next sandbox and added ReactPlayer to it, and everything works fine (both in dev and when running the built version). – PsyGik Jun 21 '21 at 10:19
  • 1
    Inspired by you, I too created a separate application and did POC for react player with dynamic import(The code in question is just a part of whole project). To my surprise, it worked. After some debugging and trial and error, I found that there was some other module which was causing error. Sorry for the inconvinience. I am marking your answer as accepted. Thank you for the support. – SANGEETH KUMAR S G Jun 21 '21 at 16:39
  • Would you mind sharing the root cause? So that if anyone faces the same problem, they have a reference. Thank you – PsyGik Jun 22 '21 at 04:13
  • I am quite not sure what is the actual issue. I was using react bootstrap navbar in the same page. I had an import for bootstrap.css file also in the same page. As part of trial and error, I commented one by one and found that bootstrap css import and navbar was causing the issue. I don't know why. Later I moved those to _app.tsx file, it worked. Seems weird for me. – SANGEETH KUMAR S G Jun 23 '21 at 05:38