Home > Back-end >  How to call an API with multiple pages using react
How to call an API with multiple pages using react

Time:06-28

I do have an API that I need to fetch into my react app. The challenge is the number of total pages doesn't exist in the API where I have the example. Also, every API call limit only gets 1000 records at a time. I do have a button that is supposed to go page by page, but I don't think it's working as it should, after all the data is fetched, it would still make an API call. My question is :

  1. How do I fetch the API so all the 5000 records will populate even though there's a limit

  2. How do I make the button to fetch API to load 1000 records at a time that doesn't disappear, because the button is buggy, when I click on it, it'll fetch 1000, but it disappears.

    const perPage = 1000;
      const [page, setPage] = useState([]);
      const [userList, setUserList] = useState([]);
      const [loading, setLoading] = useState(false);
      const [load, setload] = useState([]);
    
      useEffect(() => {
        const getUserList = () => {
          setLoading(true);
          fetch(
            `/forms?page=${page}&count=${perPage}`,
            { headers }
          )
            .then((res) => res.json())
            .then((res) => {
              //setTotalPages(res.total_pages);
              setload(res.elements);
               setUserList(res.elements);
                setPage(res.page);
              if (res.elements.length === 0) {
                console.log("array is empty");
                alert("empty");
                setLoading(false);
              }
    
              console.log(res);
            });
        };
        getUserList();
      }, [page]);
    
      return (
        <div className="App">
    
          {userList.map((x, i) => {
            return (
              <div key={i} className="box">
                <div className="name">{x.name   1} </div>
              </div>
            );
          })}
    
        <button className="btn-load-more" onClick={() => setPage(page   1)}>
          {loading ? "Loading..." : "Load More"}
        </button>
    
        </div>
      );
    
        {
        "elements": [{
        "type": "LandingPage",
        "currentStatus": "Draft",
        "id": "21",
        "createdAt": "1424811452",
        "createdBy": "5",
        "depth": "minimal",
        "folderId": "3655",
        "name": "Default Landing Page",
        "permissions": ["Retrieve", "SetSecurity", "Delete", "Update"],
        "updatedAt": "1424811452",
        "updatedBy": "5",
        "excludeFromAuthentication": "false",
        "htmlContent": {
        "type": "StructuredHtmlContent",
        "contentSource": "editor"
        },
        "relativePath": "/Default_LP"
        }, {
        "type": "LandingPage",
        "currentStatus": "Draft",
        "id": "22",
        "createdAt": "1432583568",
        "createdBy": "9",
        "depth": "minimal",
        "folderId": "3655",
        "name": "Test Landing Page",
        "permissions": ["Retrieve", "SetSecurity", "Delete", "Update"],
        "updatedAt": "1432583653",
        "updatedBy": "9",
        "excludeFromAuthentication": "false",
        "htmlContent": {
        "type": "RawHtmlContent",
        "contentSource": "upload"
        },
        "relativePath": "/Test_Landing_Page"
        }],
        "page": 1,
        "pageSize": 1000,
        "total": 5000
        } 
    

`

CodePudding user response:

To be fair, if you know that the API will always have 5 total pages and it doesn't block multiple requests, I would do it in the following way:


export default function SomeComponent(){

   useEffect(() => {
    

    (async () => {
       try {
          let promises = [];

          new Array(5).forEach(page => {
               let promise = new Promise(async (resolve, reject) => {
               try {
                   let response = await fetch('your url');
                   let json = await response.json();
                   if(response.status !== 200) reject("ERR: "   response.status)
                   resolve(json)
               }catch(err){
                   reject(err.message)
               }
            })
            promises.push(promise)
          })

         let results = await Promise.all(promises)
         //here you have all 5000 rows unless a request fails

       }catch(err){
         //handle the error
       }
     
    })()
        
   }, [])


   return (
      //your jsx here
   )
}

UPDATE I opened a sandbox and used randomuser.me which has paginated endpoints and made it work with your approach so there is probably something going wrong with the value you add in setload(). The only extra thing I did is to add an AbortController for React 18 useEffect() double render. In this example I don't have the next page number or the total but if you have them you can calculate the final request with something like

if(page === total/perPage){
 ...
}
import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  let [page, setPage] = useState(1);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    let abortController = new AbortController();
    let { signal } = abortController;
    fetch(`https://randomuser.me/api/?page=${page}&results=10`, {
      signal
    })
      .then((res) => res.json())
      .then((data) => {
        console.log(data);
        setUsers((prev) => [...prev, ...data.results]);
        if (page < 10) {
          console.log(page);
          setPage(page   1);
        }
      })
      .catch((err) => {
        if (err.name === "AbortError") {
          setUsers([]);
        }
      });

    return () => {
      abortController.abort();
    };
  }, [page]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}
  • Related