How to make pagination for two lists?

I’m trying to make pagination for two lists.
One list is showing top50 users (who like passed a line) (like ranking), it fetches data from backend, which just gives ranking <= 50, and gives it paginated, so 10 items per page.

Same applies for Others component… It’s all other users, who have ranking > 50.

But, when I click Next, it still shows one from top50…
so, I can’t manage to make it, in next page, or pagination in general for those two lists to exist. Like, if we have more than 10 of top50 list, then it should still continue showing ones from top50 items…

Here’s some frontend and backend code …

Elections.jsx

// Elections.jsx


import React, { useState, useEffect } from "react";
import axios from "axios";
import { HeaderMyProfile } from "./HeaderMyProfile";
import { Others } from "./Elections/Others";
import { Top50 } from "./Elections/Top50";
import { FormControl, InputLabel, Select, MenuItem } from "@mui/material";

let BACKEND_SERVER_BASE_URL =
  import.meta.env.VITE_BACKEND_SERVER_BASE_URL ||
  process.env.VITE_BACKEND_SERVER_BASE_URL;

const Elections = () => {
  const [userData, setUserData] = useState(null);
  const [currentUserType, setCurrentUserType] = useState(null);

  const [top50Users, setTop50Users] = useState([]);
  const [otherUsers, setOtherUsers] = useState([]);

  const [top50Page, setTop50Page] = useState(1);
  const [otherPage, setOtherPage] = useState(1);

  const [showingTop50, setShowingTop50] = useState(true);

  const [hasMoreTop50, setHasMoreTop50] = useState(true);
  const [hasMoreOthers, setHasMoreOthers] = useState(true);

  const [rankUpdated, setRankUpdated] = useState(false);

  useEffect(() => {
    const storedData =
      localStorage.getItem("authTokens") ||
      sessionStorage.getItem("authTokens");
    if (storedData) {
      const userJson = JSON.parse(storedData);
      setUserData(userJson);
      setCurrentUserType(userJson.data.user_type);
    }

    
      fetchTop50Users();
 
   
    
    
    if (!showingTop50) {
      fetchOtherUsers();
    }



  }, [top50Page, otherPage, rankUpdated, showingTop50]);

  const fetchTop50Users = async () => {
    try {
      const response = await axios.get(
        `${BACKEND_SERVER_BASE_URL}/auth/rankingTop50`,
        {
          params: {
            limit: 10,
            offset: (top50Page - 1) * 10,
          },
        }
      );
      setTop50Users(response.data);

      // Check if we should switch to showing other users
      if (response.data.length < 10) {
        setShowingTop50(false);

      }

      if (response.data.length < 10) {
        setHasMoreTop50(false);
        setShowingTop50(false);
      } else {
        setHasMoreTop50(true);
        setShowingTop50(true);
      }


    } catch (error) {
      console.error("Error fetching top users:", error);
    }
  };

  const fetchOtherUsers = async () => {
    try {
      const response = await axios.get(
        `${BACKEND_SERVER_BASE_URL}/auth/otherUsers`,
        {
          params: {
            limit: 10,
            offset: (otherPage - 1) * 10,
          },
        }
      );
      setOtherUsers(response.data);

      if (response.data.length < 10) {
        setHasMoreOthers(false);
      } else {
        setHasMoreOthers(true);
      }


    } catch (error) {
      console.error("Error fetching other users:", error);
    }
  };

  // ! with this, we need to determine...
  const handleNextPage = () => {
    if (showingTop50) {
      if (hasMoreTop50) {
        setTop50Page((prev) => prev + 1);
      } else {
        setShowingTop50(false);
        setOtherPage(1);
      }
    } else {
      if (hasMoreOthers) {
        setOtherPage((prev) => prev + 1);
      }
    }
  };

  // ! previous page
  const handlePreviousPage = () => {
    if (showingTop50 && top50Page > 1) {
      setTop50Page((prev) => prev - 1);
    } else if (!showingTop50 && otherPage > 1) {
      setOtherPage((prev) => prev - 1);
    
    } else if (!showingTop50 && otherPage === 1) {
      setShowingTop50(true);
      setTop50Page(Math.max(1, top50Page - 1));
    }
  };

  const [selectedRole, setSelectedRole] = useState("AH");

  const handleChangeRole = (event) => {
    setSelectedRole(event.target.value);
  };


  return (
    <>
      <HeaderMyProfile />
      <div className="flex m-0 flex-col">
        <FormControl
          variant="standard"
          sx={{ m: 1, minWidth: 120 }}
          className="m-4 ml-0 mb-1"
        >
          <InputLabel style={{ color: "#232323" }} id="roleDropdowns">
            <b>Selecting</b>
          </InputLabel>
          <Select
            labelId="roleDropdowns"
            value={selectedRole}
            onChange={handleChangeRole}
            className="w-[200px]"
            style={{ color: "#000" }}
          >
            <MenuItem value={"AH"}>Athletes</MenuItem>
            <MenuItem value={"GP"}>Global President</MenuItem>
            <MenuItem value={"RS"}>Referee & support</MenuItem>
          </Select>
        </FormControl>
      </div>
   
  
      <div className="mt-8">
        <table className="w-full">
          <thead>
            <tr>
              <th className="w-[18%]">Rank</th>
              <th className="w-[15%]">Name</th>
              <th className="w-[8%]">Age</th>
              <th className="w-[12%]">Country</th>
              <th className="w-[27%]">Email</th>
              <th className="w-[20%]">Phone</th>
            </tr>
          </thead>
          <tbody>
            {top50Users.map((user, index) => (
              <Top50
                userId={user.userId}
                rank={user.ranking}
                name={user.name}
                age={user.age}
                country={user.country}
                email={user.email}
                phone={user.phone}
                user_type={currentUserType}
                index={index}
                lastIndex={top50Users.length - 1}
                setRankUpdated={setRankUpdated}
              />
            ))}

            {!showingTop50 && otherUsers.length > 0 && (
              <>
                <tr
                  className="border-b-2 border-red_first "
                  style={{ padding: "0px", paddingTop: "-5px" }}
                >
                  <td colSpan="100%"></td>
                </tr>
              </>
            )}

            {!showingTop50 &&
              otherUsers.map((user, index) => (
                <Others
                  userId={user.userId}
                  rank={user.ranking}
                  name={user.name}
                  age={user.age}
                  country={user.country}
                  email={user.email}
                  phone={user.phone}
                  user_type={currentUserType}
                  index={index}
                  lastIndex={top50Users.length - 1}
                  setRankUpdated={setRankUpdated}
                
                />
              ))}
          </tbody>
        </table>
      </div>
    

      <div className="flex justify-center mt-4">
        <button
          disabled={(showingTop50 && top50Page === 1) || (!showingTop50 && hasMoreOthers)}
          onClick={handlePreviousPage}
          className="px-4 py-2 bg-blue-500 text-white rounded mr-4"
        >
          Previous
        </button>
        <button
          disabled={(showingTop50 && !hasMoreTop50) || (!showingTop50 && !hasMoreOthers)}
          onClick={handleNextPage}
          className="px-4 py-2 bg-blue-500 text-white rounded"
        >
          Next Page
        </button>
      </div>
      <p className="m-2">
        You are selecting the athletes to compete in the next games. The{" "}
        <span className="text-red_first">top 50</span> athletes in the list will
        be chosen to participate in the games. Use the Update Rank feature to
        increase or decrease the rank of each athlete.
      </p>
    </>
  );
};

export { Elections };

Top50.jsx and Others.jsx is same layout…




import Popup from "reactjs-popup";
import "reactjs-popup/dist/index.css";
import React, { useState, useRef, useEffect } from "react";
import axios from "axios";

import { Button } from "@mui/material";

const Top50 = ({
  rank,
  name,
  age,
  country,
  email,
  phone,
  user_type,
  index,
  lastIndex,
  setRankUpdated,
  userId,
}) => {


  const [currentRank, setCurrentRank] = useState(rank);

  const popupRef = useRef(null);

  let BACKEND_SERVER_BASE_URL =
    import.meta.env.VITE_BACKEND_SERVER_BASE_URL ||
    process.env.VITE_BACKEND_SERVER_BASE_URL;

  
  const [userData, setUserData] = useState(null);
  const [original_email, setOriginalEmail] = useState(null);

  useEffect(() => {
    const storedData =
      localStorage.getItem("authTokens") ||
      sessionStorage.getItem("authTokens");
    if (storedData) {
      var userJson = JSON.parse(storedData);

      setUserData(userJson);

      setOriginalEmail(userJson.data.email);
    }
  }, []);

  const increaseRank = () => {
    setCurrentRank(currentRank + 1);
  };

  const decreaseRank = () => {
    setCurrentRank((prevRank) => (prevRank > 1 ? prevRank - 1 : 1));
  };

  const cancel = () => {
    setCurrentRank(rank); // just revert it

    // and exit popup
    popupRef.current.close();
  };

  const saveChanges = async () => {
    try {
      var response = await axios.post(
        `${BACKEND_SERVER_BASE_URL}/auth/update_rank_data`,
        {
          userId,

          originalRank: rank,
          goingToRank: currentRank,
        }
      );

      if (response.status === 200) {
        setRankUpdated((prev) => !prev);
        setCurrentRank(rank); //bring it back to original...
        popupRef.current.close();
      }
    } catch (error) {
      console.log("sta je");
      console.log(error);
    }
  };

  return (
    <>
       <tr key={index}>
        {user_type === "NP" ? (
          <>
           
            <td className="flex gap-2 justify-start items-center">
              <div>
                <p>{rank}</p>
              </div>
              <div>
              
                <Popup
                  ref={popupRef}
                  trigger={
                    <p className="cursor-pointer select-none text-gray_first">
                      Update Rank{" "}
                      <img
                        src="myaccount/pencil.svg"
                        style={{
                          width: "10px",
                          height: "10px",
                          display: "inline-block",
                          marginBottom: "5px",
                        }}
                      />
                    </p>
                  }
                  position="right center"
                  contentStyle={{ width: "auto" }}
                >
                  <div className="m-4">
                    <div className="flex gap-2 mb-2">
                      <p>Current rank</p>
                      <p>
                        <b>{currentRank}</b>
                      </p>
                    </div>

                    <div className="flex gap-2">
                      <p>Update rank</p>

                      <div className="flex justify-center items-center gap-2">
                        <Button
                          onClick={increaseRank}
                          className="w-[15px]"
                          style={{ marginTop: "0px", padding: "0px" }}
                          sx={{
                            height: "15px",
                            bgcolor: "#fff",
                            color: "#232323",
                            borderRadius: 15,
                            border: `1px solid #AF2626`,
                            "&:hover": {
                              background: "rgb(196, 43, 43)",
                              color: "white",
                              border: `1px solid rgb(196, 43, 43)`,
                            },
                          }}
                        >
                          <span className="popins-font">+</span>
                        </Button>
                        <Button
                          onClick={decreaseRank}
                          className="w-[15px]"
                          style={{ marginTop: "0px", padding: "0px" }}
                          sx={{
                            height: "15px",
                            bgcolor: "#fff",
                            color: "#232323",
                            borderRadius: 15,
                            border: `1px solid #AF2626`,
                            "&:hover": {
                              background: "rgb(196, 43, 43)",
                              color: "white",
                              border: `1px solid rgb(196, 43, 43)`,
                            },
                          }}
                        >
                          <span className="popins-font">-</span>
                        </Button>
                      </div>
                    </div>

                    <div className="flex justify-center items-center gap-2 m-4">
                      <Button
                        onClick={cancel}
                        className="w-[85px]"
                        style={{ marginTop: "0px", padding: "0px" }}
                        sx={{
                          fontSize: "8pt",
                          height: "30px",
                          bgcolor: "#fff",
                          color: "#232323",
                          borderRadius: 15,
                          border: `1px solid #fff`,
                          "&:hover": {
                            background: "rgb(196, 43, 43)",
                            color: "white",
                            border: `1px solid rgb(196, 43, 43)`,
                          },
                        }}
                      >
                        <span className="popins-font">Cancel</span>
                      </Button>

                      <Button
                        onClick={saveChanges}
                        className="w-[120px]"
                        style={{ marginTop: "0px", padding: "0px" }}
                        sx={{
                          fontSize: "8pt",
                          height: "30px",
                          bgcolor: "#AF2626",
                          color: "#fff",
                          borderRadius: 15,
                          border: `1px solid #AF2626`,
                          "&:hover": {
                            background: "rgb(196, 43, 43)",
                            color: "white",
                            border: `1px solid rgb(196, 43, 43)`,
                          },
                        }}
                      >
                        <span className="popins-font">Save changes</span>
                      </Button>
                    </div>
                  </div>
                </Popup>
              </div>
            </td>
          
          </>
        ) : (
          <>
     
            <td className="flex gap-2 justify-start">
              <p>{rank}</p>
            </td>
            {/* </div> */}
          </>
        )}

        <td>{name}</td>
        <td>{age}</td>
        <td>{country}</td>
        <td>{email}</td>
        <td>{phone}</td>
      </tr>


      {index !== lastIndex && (
        <tr>
          <td colSpan="6">
            <hr />
          </td>
        </tr>
      )}
      {/* <hr /> */}
    </>
  );
};

export { Top50 };

Backend code (nodejs, sequelize ORM):

const rankingTop50 = async (req, res) => {
  const limit = parseInt(req.query.limit) || 10;
  const offset = parseInt(req.query.offset) || 0;

  try {
    const topUsers = await User.findAll({
        where: {
            ranking: {
                [Op.lte]: 50 // Fetch users with ranking less than or equal to 50
            }
        },
        order: [['ranking', 'ASC']], 
        limit: limit,
        offset: offset
    });

    res.json(topUsers);

} catch (error) {
  console.error('Error fetching top users:', error);
  res.status(500).json({ error: 'Internal server error' });
}

}


const otherUsers = async (req, res) => {
  const limit = parseInt(req.query.limit) || 10; 
  const offset = parseInt(req.query.offset) || 0;

  try {
    const otherUsers = await User.findAll({
        where: {
            ranking: {
                [Op.gt]: 50 // Fetch users with ranking greater than 50
            }
        },
        order: [['ranking', 'ASC']], 
        limit: limit,
        offset: offset
    });

   
    res.json(otherUsers);

} catch (error) {
  console.error('Error fetching top users:', error);
  res.status(500).json({ error: 'Internal server error' });
}

}

And few images how it should look:

this is when it crosses the line. first page for both

and this is on next page. it should display ONLY others. because there’s no more than 10 elements in Top50… so no need to show it on next page… but I don’t know how

and this is, when I make that item bigger than 50 (ranking), so it only shows , which means pagiantion works nice, it’s just hard to combine them together…