user12893845
user12893845

Reputation: 176

how to make API request only once for each page in REACT INFINITE scroll here

I am working on one React infinite scroll component and its working fine and perfectly, only one small issue is that for page number 1, it makes twice the API request, which I do not want and for other pages ex 2,3,4 it makes request only once.

I tried everything but i am unable to modify the code so that for page number 1,it makes only once the request.

How can i make only once the request for page number 1 also ? here is the working code.

import React, { useState, useEffect } from "react";
import axios from "axios";

function Lists() {
  const [posts, setPosts] = useState([]);
  const [newposts, setNewposts] = useState([]);
  const [isFetching, setIsFetching] = useState(false);
  const [page, setPage] = useState(1);
  const LIMIT = 7;

  const getPosts = () => {
    axios.get(`https://jsonplaceholder.typicode.com/posts?_limit=${LIMIT}&_page=${page}`)
      .then(res => {
        setNewposts(res.data);
        setPosts([...posts, ...res.data]);
        setIsFetching(false);
      })
  };

  const getMorePosts= () => {
      setPage(page + 1);
      getPosts();
  }

  const handleScroll = () => {
    if (
      window.innerHeight + document.documentElement.scrollTop  !==
      document.documentElement.offsetHeight
    ) return;
    setIsFetching(true);
  }

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

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

  useEffect(() => {
    if (!isFetching){
      return;
    }
    if( newposts.length > 0 ){
        getMorePosts();
    }
  }, [isFetching]);

  return (
    <div className="App">
      {posts.map((post, index) => (
        <div key={index} className="post">
          <div className="number">{post.id}</div>
          <div className="post-info">
            <h2 className="post-title">{post.title}</h2>
            <p className="post-body">{post.body}</p>
          </div>
        </div>
      ))}
      {isFetching && newposts.length > 0 && (
        <div style = {{display: "flex", justifyContent:"center"}}>
        <div className="spinner-border" role="status">
            <span className="sr-only">Loading...</span>
        </div>
        </div>
      )}
    </div>
  );
 }

export default Lists;


Upvotes: 1

Views: 2140

Answers (1)

davnicwil
davnicwil

Reputation: 30967

The issue is that setPage, like any state setter, works asynchronously - it doesn't update the value of page immediately like it appears you might be expecting it to. The value of page will only be updated in a subsequent render which is triggered by the state change.

That means that in getMorePosts() you're not really getting page + 1, you're just getting page.

That means on first render you're calling getPosts() in that other useEffect, with page set to 1, then calling it again when you scroll and call getPosts() inside the getMorePosts() call.

Subsequent calls only happen once because getMorePosts() increments page in every subsequent render.

As for a fix, a quick one might be to just take page as an arg to getPosts, then you can statically call getPosts(1) on first render and keep the rest of the logic the same but init the page state to 2 and change the call to getPage(page) inside getMorePosts().

Upvotes: 1

Related Questions