1

I'd like to implement pagination by having a button to load more data from youtube api. To go to next/ previous pages, youtube api require pagetoken. As a beginner, while I know how to fetch the initial data, I don't know how to load more data using pagetoken in a useEffect hook. I thought if I create onClick event on the button and pass the initial fetch function in it, that would solve the problem but it doesn't. So here's my code :

function VideoList() {
  const [videoList, setvideoList] = useState([]);
  const [pageToken, setPageToken] = useState("");

  const baseURL = `https://youtube.googleapis.com/youtube/v3/activities?part=snippet`;
  const maxResults = `maxResults=20`;
  const channelId = `channelId=${process.env.REACT_APP_CHANNEL_ID}`;
  const apiKey = `key=${process.env.REACT_APP_API_YOUTUBE}`;
  const request = `${baseURL}&${channelId}&${apiKey}&${maxResults}${pageToken}`;

  const fetchVideos = async () => {
    try {
      const response = await axios.get(request);
      const responseResult = response.data;
      setvideoList(responseResult.items);
      setPageToken(`&pageToken=${responseResult.nextPageToken}`);
    } catch (error) {
      console.log(error.message);
    }
  };

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

  if (videoList.length === 0) {
    return null;
  }

  return (
    <>
      <NavBar />
      <div className="m-8">
        <VideoCard videos={videoList} />
        <button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full shadow"
          onClick={fetchVideos()}
        >
          More Videos
        </button>
      </div>
      <SiteFooter />
    </>
  );
}

export default VideoList;

However, when I click the button, the following error happened

Warning: Expected onClick listener to be a function, instead got a value of object type.

Any idea how to correctly implement this? Help would be very much appreciated and thank you in advance!

3 Answers3

3

I think the only thing you need is merge the old state with the new state:

 setvideoList((oldData) => [...oldData, ...responseResult.items]);

This is the link to React useState hook

blurk
  • 235
  • 2
  • 6
  • 1
    Thank you, this is amazing. Only weird thing is that, after the initial 20 videos are fetched, click the button and refresh the page, the first 20 videos is displayed again so that there are 2 sets of the initial 20 videos – Dimas Rizqi Aug 29 '22 at 09:47
  • Oh yeah, I think it's because of the new React 18 Strict Mode: the code in useEffect will be called twice. Here is [an answer](https://stackoverflow.com/a/60619061/13329040) on Stack overflow, you will want to read it. The simplest way to solve it is to comment out or remove the `` tag in `index.js`. – blurk Aug 29 '22 at 10:43
1

I would do it like this (pseudocode):

const [pagination, setPagination] = useState(0);

const nextPage = () => {
   setPagination(pagination + 1);
}

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

onClick={nextPage}

Your onclick handler kind of depends if its a next arrow or if you click on an actual number, but it is all the same really, just need to write a handler for that.

Now everytime you change the pagination info, you will fetch the videos according to the pagination, you can also make pagination an object if you want to have multiple properties like itemsperpage, page etc.

EDIT:

Oh and regarding your error, you have to pass in the function name without parentheses.

onClick={fetchVideos} not onClick={fetchVideos()}

niklasbec
  • 834
  • 5
  • 12
  • The thing about the youtube api is that the pagination needs to refer to nextPageToken and previousPageToken from the previous api request so we can't simply +1 the fetch function. Removing the parentheses solves it by the way, so thank you for that! However, after clicking the button 2 times, it no longer works and the returning pagetoken is undefined. Any idea why? – Dimas Rizqi Aug 29 '22 at 09:31
1

Since the API request remains the same except for the pageQuery I would pass that in as a parameter to the function.

 const fetchVideos = async (token) => {
  const request = `${baseURL}&${channelId}&${apiKey}&${maxResults}${token}`;

    try {
      const response = await axios.get(request);
      const responseResult = response.data;
      setvideoList(responseResult.items);
      setPageToken(`&pageToken=${responseResult.nextPageToken}`);
    } catch (error) {
      console.log(error.message);
    }
  };

Then we can use a useEffect to check for change in your local pageQuery state. We pass page token into the dependancy array (the array passed into the end of the function) which tells react to check for any changes in this property; if there is a change it will re-run everything within the useEffect hook (e.i, repeat the API request with the new value of pageToken)

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

Then in your onClick event handler you can update the state of pageToken. You've currently set it to an empty string but it might make more sense to set it to the number 0 and then add 1 on each click.

<button
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full shadow"
          onClick={()=> { // update pagination }}
        >

msmoore
  • 396
  • 2
  • 12
  • Would the +1 on OnClick handler work if the pageToken is a random string? Example of the page token is CCgQAA – Dimas Rizqi Aug 29 '22 at 09:53
  • Yes. The useEffect will check for any change in pageToken. If page token is 'CCgQAA' and then changes to 'JDKSkU' the function will re-run. – msmoore Aug 29 '22 at 10:09