react-player icon indicating copy to clipboard operation
react-player copied to clipboard

Error: Super expression must either be null or a function on NextJS 13

Open feri-irawan opened this issue 2 years ago • 7 comments

Current Behavior

i just wrote one line of code like the one in the example. Then I got an error like the issue title

Expected Behavior

No errors

Steps to Reproduce

  1. Import ReactPlayer
  2. <ReactPlayer url={youtubeURL} />
  3. npm run dev

Environment

  • URL attempting to play: localhost
  • Browser: edge
  • Operating system: windows 10
  • jsFiddle example: -
  • framework: NextJS 13 using app directory

Other Information

feri-irawan avatar Jan 22 '23 15:01 feri-irawan

@feri-irawan Are you instantiating the component in a "use client" component? Doing that solves the issue for me but I run into hydration issues shortly.

lundjrl avatar Feb 04 '23 17:02 lundjrl

Adding the 'use client' directive at the top of the file solves the initial error and produces a new one, as mentioned by @lundjrl, regarding the component’s hydration.

By utilizing the useEffekt() hook I managed to get external sources to work properly (tested for YouTube and Vimeo):

import { useEffect, useState } from 'react'
import ReactPlayer from 'react-player'

export default function Figure({ video }) {
  const [isLoaded, setIsLoaded] = useState(false)

  useEffect(() => {
    setIsLoaded(true)
  }, [])

  return(
    {isLoaded ? (
      <ReactPlayer
        url={video?.source}
      />
    ) : null}
  )
}

But when the url prop is sourced from /public or a CDN (like Sanity) then the <video> element’s src attribute remains undefined. Any ideas on why this could be happening?

sebastianhaiss avatar Feb 05 '23 14:02 sebastianhaiss

@sebastianhaiss Thank you, this helped.

njoguamos avatar Feb 17 '23 21:02 njoguamos

Adding the 'use client' directive at the top of the file solves the initial error and produces a new one, as mentioned by @lundjrl, regarding the component’s hydration.

By utilizing the useEffekt() hook I managed to get external sources to work properly (tested for YouTube and Vimeo):

import { useEffect, useState } from 'react'
import ReactPlayer from 'react-player'

export default function Figure({ video }) {
  const [isLoaded, setIsLoaded] = useState(false)

  useEffect(() => {
    setIsLoaded(true)
  }, [])

  return(
    {isLoaded ? (
      <ReactPlayer
        url={video?.source}
      />
    ) : null}
  )
}

But when the url prop is sourced from /public or a CDN (like Sanity) then the <video> element’s src attribute remains undefined. Any ideas on why this could be happening?

It works for me :)

rubenofen avatar Mar 31 '23 12:03 rubenofen

'use client'

import Image from 'next/image';
import styles from '../.././Page.module.css';
import data from '../.././data.json';
import Navbar from '../.././components/Navbar';
import Link from 'next/link';
import ReactPlayer from 'react-player'
import { useState, useEffect } from 'react';

export default function Page({ params }) {
  const [isLoaded, setIsLoaded] = useState(false);

  useEffect(() => {
    setIsLoaded(true)
  }, [])

  return (
    <div>
      <Navbar />
      <h1
        className='text-7xl text-center tracking-tighter font-extrabold mb-14 mt-10'
        id={styles.heading}
      >
        VideoCourse
      </h1>
      <div className='flex items-center place-content-evenly flex-wrap'>
        {data[params.course - 1]['video_links'].map((video, index) => (
          <div className='flex flex-col mb-10 items-center border-2 cursor-pointer hover:border-zinc-300 solid h-96 w-80 border-zinc-700 rounded-xl' key={index + 1}>
            {isLoaded ? (
              <ReactPlayer
                url={video?video:null}
              />
            ) : null}
          </div>
        ))}
      </div>
    </div>
  );
}

this works for me

niladrix719 avatar Jul 29 '23 07:07 niladrix719

An alternative, debatably cleaner approach is to wrap react player in its own 'use client' component. I'm new to server components (maybe everyone is?) and co-workers keep pointing me to this route. One wrote this decent post on the subject.

player.js

'use client';

import ReactPlayer from 'react-player';

export default (props) => {
  return <ReactPlayer {...props} />;
};

other file

import Player from 'player.js';
...
<Player url="..." />

heff avatar Aug 11 '23 23:08 heff

Rather than using state, this seems to be a good case to use next's dynamic so it's using suspense:

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

type VideoPlayerProps = FilePlayerProps & {
  className?: string
}

export const VideoPlayer = ({ className, ...rest }: VideoPlayerProps) => {
  return (
    <div className={twMerge('aspect-video', className)}>
      <ReactPlayer width="100%" height="100%" {...rest} />
    </div>
  )
}

jedimonkey avatar Aug 17 '23 06:08 jedimonkey