Vic
Vic

Reputation: 13

react-youtube TypeError: Cannot read properties of null (reading 'playVideo')

Unhandled Runtime Error TypeError: Cannot read properties of null (reading 'playVideo')

Call Stack eval node_modules/react-youtube/node_modules/youtube-player/dist/index.js (65:0) new Promise

exports.default node_modules/react-youtube/node_modules/youtube-player/dist/index.js (64:0) createPlayer node_modules/react-youtube/dist/YouTube.esm.js (170:41)

https://upload.cc/i1/2023/02/26/daWAmu.png

This error may occur when I click pagination to get the video or just load the page,I show three videos on page, and change the page when want to get more. Sorry for the English not my native language.

React 18.2.0 Next 13.1.6 react-youtube 10.1.0

const Playlist = ({ playlistId }) => {
  const [videos, setVideos] = useState([]);
  const [currentPage, setCurrentPage] = useState(0);
  const isMobile = useMediaQuery({ maxWidth: 640 });
  const isPad = useMediaQuery({ maxWidth: 1180 });
  const videosPerPage = isMobile ? 1 : isPad ? 2 : 3;

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(
          `https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=${playlistId}&key=${process.env.NEXT_PUBLIC_ytAPI}`
        );
        const data = await response.json();
        setVideos(data.items);
        console.log('data: ', data);
      } catch (error) {
        console.log(error);
      }
    };
    fetchData();
  }, [playlistId]);

  const handlePageClick = ({ selected }) => {
    setCurrentPage(selected);
  };

  const currentVideos = useMemo(() => {
    const offset = currentPage * videosPerPage;
    return videos.slice(offset, offset + videosPerPage);
  }, [currentPage, videos, videosPerPage]);

  const getOpts = useCallback(() => {
    return {
      playerVars: {
        autoplay: 0,
      },
    };
  }, []);

  const opts = getOpts();

  return (
    <>
      <div className='flex container justify-center my-10'>
        {currentVideos.map((video) => (
          <div
            key={video.id}
            className='w-full'
          >
            <YouTubePlayer
              className='video-container'
              videoId={video.snippet.resourceId.videoId}
              opts={opts}
            />
            <h2 className='text-center m-4'>{video.snippet.title}</h2>
          </div>
        ))}
      </div>
      <ReactPaginate
        className='item flex w-full justify-center mb-8'
        previousLabel={'« prev'}
        nextLabel={'next »'}
        breakLabel={'...'}
        marginPagesDisplayed={0}
        activeClassName={'active'}
        breakClassName={'disabled'}
        containerClassName={'pagination'}
        pageCount={Math.ceil(videos.length / videosPerPage)}
        onPageChange={handlePageClick}
        forcePage={currentPage}
        pageRangeDisplayed={3}
      />
    </>
  );
};

export default Playlist;

I tried to replace react-youtube with react-player but got the same error message so I think it might not be caused by npm package

Upvotes: 1

Views: 1136

Answers (1)

Sebi
Sebi

Reputation: 460

You probably have to wait for the fetch request to the API. You can restructure your code like this:

const Playlist = ({ playlistId }) => {
  const [videos, setVideos] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  // ...

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(
          `https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=${playlistId}&key=${process.env.NEXT_PUBLIC_ytAPI}`
        );
        const data = await response.json();
        setVideos(data.items);
        setIsLoading(false); // set isLoading to false after data is fetched
      } catch (error) {
        console.log(error);
        setIsLoading(false); // also set isLoading to false on error
      }
    };
    fetchData();
  }, [playlistId]);

  // ...

  return (
    <>
      {isLoading ? (
        <div>Loading...</div>
      ) : (
        <div className='flex container justify-center my-10'>
          {currentVideos.map((video) => (
            <div key={video.id} className='w-full'>
              <YouTubePlayer
                className='video-container'
                videoId={video.snippet.resourceId.videoId}
                opts={opts}
              />
              <h2 className='text-center m-4'>{video.snippet.title}</h2>
            </div>
          ))}
        </div>
      )}
      {/* ... */}
    </>
  );
};

Now it shows Loading... until you get a response from the API.

Upvotes: 0

Related Questions