Front End Development And Integration With Google API | Using Hooks, Throttle, And Dynamic Style.

Front End Development And Integration With Google API | Using Hooks, Throttle, And Dynamic Style.

This article can be better understood if you are an intermediate React and familiar with API Calls.

In this article, I would like to go over in how to solve the problem of fetching only the videos that an user can see within the application UI. Basically, the problem is you have to fetch videos in a perfect matrix grid( rows and columns are the same). The challenge is that the number of rows and grids change as screen reduces. Mostly, only videos that can be fit in the grid it should be fetched. With this in mind we are implementing a dynamic UI where only a certain amount of videos are fetched within a screen size. And where do I come with this ? If you open the youtube application and set at full screen at home page you ll notice that the top sub grid have 5 rows and 5 columns and the number of videos change while maintaining an even distribution.

Approach To The Problem:

Controlling the number of Videos to fetch And Implementing useYoutubeVideos Hook:

When we call the Youtube API , provided by Google, we are able to add a max limit of videos to fetch by querying with "<maxResults>=<Number>". Having this ability to select max we can dynamically change the value to fit our needs. When we calling an API within our application where we expecting more than just saving the data, we can create a hook and in the hook we will handle all the functionality to handle the fetching and returning the state needed.

Now, how are we going to implement the hook. First thing we need to know what is the form of data we are expected to receive. This is a must do and yes I am using Typecript. So, before get into the documentation the most common sense is that we want to return many videos and the a good data structure to store are arrays. Thus, we expecting Videos [] of type Videos but we have no idea what is the type Video. After exploring the documentation we learn that a video is

{
  id: {
    videoId: string;
  };
  snippet: VideoSnippet;
}
        

And Video snipped is

{
  title: string;
  description: string;
  thumbnails?: {
    default?: {
      url: string;
    };
    medium?: {
      url: string;
    };
    high?: {
      url: string;
    };
  };
}        


there is nothing complex about the data structure for the api and we are given several options to the video thumbnails.


In addition to this, were are given more parameters such loading and error. Another important functionality is we need to play a video but to play a video we need a function that gets the video id. Now, we can formulate our hook as a function, in this case we need to pass the API key from google console, and max results. But our result will be of

{
  videos: Video[];
  loading: boolean;
  error: string | null;
  playVideo: (videoId: string) => void;
  selectedVideoId: string | null;
}        

Here videos is the array video or we can put as Array<Video>. We need to have loading to add some UI while loading, error to handle errors, play video and selected video to handle videos selected to play. Thus we will be returning the above to use later. We also need to set each as a state so we will be passing the state result. Mostly, we will be passing an async function to fetch and call only once using use effect. The loading is initially assume to be true and within the fetch function if we get a response status 200 then we can set loading to false. Here is the final hook implementation.

Youtube API End Point: https://www.googleapis.com/youtube/v3/search?key=${apiKey}&part=snippet&type=video&maxResults=${maxResult},

eEffect, useState } from 'react';
import axios from 'axios';

export interface VideoSnippet {
  title: string;
  description: string;
  thumbnails?: {
    default?: {
      url: string;
    };
    medium?: {
      url: string;
    };
    high?: {
      url: string;
    };
  };
}

export interface Video {
  id: {
    videoId: string;
  };
  snippet: VideoSnippet;
}

interface UseYoutubeVideosResult {
  videos: Video[];
  loading: boolean;
  error: string | null;
  playVideo: (videoId: string) => void;
  selectedVideoId: string | null;
}

export default function useYoutubeVideos(
  apiKey: string,
  maxResult: number,
): UseYoutubeVideosResult {
  const [videos, setVideos] = useState<Video[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);
  const [selectedVideoId, setSelectedVideoId] = useState<string | null>(null);

  function playVideo(videoId: string): void {
    setSelectedVideoId(videoId);
  }

  async function fetchVideos() {
    setLoading(true);
    setError(null);

    try {
      const response = await axios.get(
        `https://www.googleapis.com/youtube/v3/search?key=${apiKey}&part=snippet&type=video&maxResults=${maxResult}`,
      );

      if (response.status === 200) {
        setVideos(response.data.items);
      }
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    fetchVideos();
  }, []);

  return {
    videos,
    loading,
    error,
    playVideo,
    selectedVideoId,
  };
}        


Implementing UseVideoGrid hook:

The idea is to have hook that will check the width of screen and based on the width it will set the state to the number of videos per row. Here is where we would use a throttle function for performance. We will then be returning the state videos per row.


Hook Implementation

The first step is to get the screen width with window.innerWidth and set a basic if and else condition where we will use a state videosPerRow <number> (5) default value set to 5.

  const [videosPerRow, setVideosPerRow] = useState<number>(5);

  const determineVideosToShow = () => {
    const screenWidth = window.innerWidth;

    if (screenWidth <= 500) {
      setVideosPerRow(1); // 1 video per row on very small screens (2 rows total)
    } else if (screenWidth > 500 && screenWidth <= 739) {
      setVideosPerRow(2); // 2 videos per row (2 rows total)
    } else if (screenWidth >= 740 && screenWidth <= 1023) {
      setVideosPerRow(3); // 3 videos per row (2 rows total)
    } else if (screenWidth >= 1024 && screenWidth <= 1279) {
      setVideosPerRow(4); // 4 videos per row (2 rows total)
    } else {
      setVideosPerRow(5); // 5 videos per row (2 rows total)
    }
  };        



we put the logic inside a nested function so we can call it inside the throttle as a call back function. I have set the time to 150 mile seconds to keep its responsiveness intact, otherwise, it will not be as responsive as needed when interacting with screen size.

 // Adding the throttle function here with 150 seconds time out
  const throttleVideosToShowPerRow  = useThrottle(determineVideosToShow, 150);        


import { useEffect, useState } from 'react';
import { useThrottle } from './useThrottle.ts';

/**
 * @videosPerRow{number} return number of videos show per row.
 * @setVideosPerRow{void} function that uses screen width to set number of videos to show
 * @return is the number of videos per row to be used
 */
export const useVideoGrid = () => {
  const [videosPerRow, setVideosPerRow] = useState<number>(5);

  const determineVideosToShow = () => {
    const screenWidth = window.innerWidth;

    if (screenWidth <= 500) {
      setVideosPerRow(1); // 1 video per row on very small screens (2 rows total)
    } else if (screenWidth > 500 && screenWidth <= 739) {
      setVideosPerRow(2); // 2 videos per row (2 rows total)
    } else if (screenWidth >= 740 && screenWidth <= 1023) {
      setVideosPerRow(3); // 3 videos per row (2 rows total)
    } else if (screenWidth >= 1024 && screenWidth <= 1279) {
      setVideosPerRow(4); // 4 videos per row (2 rows total)
    } else {
      setVideosPerRow(5); // 5 videos per row (2 rows total)
    }
  };

  // Adding the throttle function here with 150 seconds time out
  const throttleVideosToShowPerRow = useThrottle(determineVideosToShow, 150);

  // use effect to change the state whenever
  useEffect(() => {
    throttleVideosToShowPerRow();

    const handleVideosToShow = () => {
      throttleVideosToShowPerRow();
    };

    window.addEventListener('resize', handleVideosToShow);

    return () => window.removeEventListener('resize', handleVideosToShow);
  }, [throttleVideosToShowPerRow]);

  return videosPerRow;
};
        

Putting All together


Now, we are going to call these hooks in the component that represents the display for the videos. First, we will use a dummy_data before fetching the api since theres a limit to how much we can request per day.

import dummy_videos from '../../../dummyData.json';        
  // Using the useVideo hook to control number of videos show per screen size
  const videosPerRow = useVideoGrid();

  const totalVideosToShow = videosPerRow * 2;        

The idea is that we want both rows to have same amount of videos so we multiply by 2 since we are dealing with only two rows


We then call the youtube vidoes use hook and destruct elements we need to use

const { videos, loading, error, playVideo, selectedVideoId } =
  useYoutubeVideos(api_key, totalVideosToShow);        

we then implement a functio to play videos by calling the deconstructed function from hoo,

function handleVideoClick(videoId: string) {
  playVideo(videoId);
}        

Now we add the dynamic values in the div for "gridTemplateColumns" we basically repeating columns per video per row and set min and max (0 as no videos and 1fr as 1 video. The main div holds 2 inner divs. One div where we have the grid layout for the videos and another div where we map the videos.

 return (
    <>
      {/* Main Home Frame */}
      <div className="h-screen overflow-hidden flex justify-center items-start ">
        {!isLoggedIn && <NotLoggedInBanner />}

        {/* first row of videos */}
        <div
          className={` h-[600px] w-full  grid  grid-rows-2  gap-4 p-4  overflow-hidden `}
          style={{
            gridTemplateColumns: `repeat(${videosPerRow},minmax(0,1fr))`,
          }}
        >
          {dummy_videos.videos.slice(0, totalVideosToShow).map((video) => (
            <div
              key={video.id.videoId}
              className="flex  flex-col justify-center items-center rounded-lg border "
            >
              {/*{selectedVideoId === id.videoId ? (*/}
              {/*  <iframe*/}
              {/*    width="560"*/}
              {/*    height="315"*/}
              {/*    src={`https://www.youtube.com/embed/${id.videoId}`}*/}
              {/*    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"*/}
              {/*    allowFullScreen*/}
              {/*    title="YouTube Video Player"*/}
              {/*  ></iframe>*/}
              {/*) : (*/}
              <img
                src={video.snippet.thumbnails?.default?.url}
                alt={video.snippet.title}
                onClick={() => handleVideoClick(video.id.videoId)}
                className="invert pointer"
              />
              {/*)}*/}
              <div className="font-bold text-lg  text-center">
                {video.snippet.title}
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
};        

The video play section is commented out and not part of this article. Yet, in the <img/> tag we setting the video thumbnails and to default size.


the result we get with dummy data. Note we are only calling the amount of videos based on screen size.



Final Steps:

Now that we verified, we can make a console log where we printing the length after slicing, we can now use the actual API, add some TailwindCss Styles and test it.





Alright, this is the end of the article. The functionality can be better and current one is causing some of videos to collapse but using CSS "flex-wrap" and "padding" we are able to fix this issue.

Who Am I

My name is Sharif and I have a B.S degree in computer science. Since graduation, I have been mostly focus on full stack development. In addition, I like to solve algorithms questions like leet code. I have done few assessments by big tech companies and currently in the interview process, being as positive as I can. My next academic step in a near future is focus on machine learning since I am strong believer of using it to create apps with a vast arrays of functionalities. This article is part of my personal project where I am building a full stack clone of youtube. If you like my article please give me thumbs up. Soon, I will make these articles also available on Dev Community.


要查看或添加评论,请登录

Mohamed Sharif的更多文章

  • A Solution for DNS Leaks Using Kali Linux

    A Solution for DNS Leaks Using Kali Linux

    Disclaimer: This is for educational purposes to learn how to properly configure a vpn. This should not be used with the…

  • An Easier explanation for AVL Trees And Implemented Using Python

    An Easier explanation for AVL Trees And Implemented Using Python

    How To Find Parent And Children When An Array Is Given AVL trees might seem more complicated than what it seems but if…

  • Implementing A Binary Heap Using Python

    Implementing A Binary Heap Using Python

    Binary heaps are a form of binary tree but the only order that we follow is that the parent value should be either…

  • Identifying Node's Relations From Array For A Complete Binary Tree

    Identifying Node's Relations From Array For A Complete Binary Tree

    I would like to make this article more conceptual and to revise the relation of the current node value to its left…

  • 1064. Fixed Point

    1064. Fixed Point

    https://leetcode.com/problems/fixed-point/solutions/5862817/use-binary-search-and-if-value-found-recurse-to-the-left-sin…

  • Odd Even Linked List

    Odd Even Linked List

    In this article I would like to go over a solution to solve leet code problem 328. Odd Even Linked List.

  • 1122. Relative Sort Array

    1122. Relative Sort Array

    In this problem we are given 2 arrays where the first array contains all the numbers that the second array has but the…

  • 461. Hamming Distance

    461. Hamming Distance

    In this article we are going through the Leet Code Hamming Distance problem. We are given 2 integer numbers and we are…

  • Leaf-Similar Trees

    Leaf-Similar Trees

    I will be walking through the algorithm to solve the problem of 2 nodes where we are told to find if the leaf of both…

  • Solving Leet Code 243. Shortest Word Distance

    Solving Leet Code 243. Shortest Word Distance

    In this problem, we are given a array with words and there are 2 words that already exist and the goal is to return the…

社区洞察

其他会员也浏览了