skip to Main Content

i created a simple pagingation in react and using JSONPlaceHolder fake API. the given data shown in material UI components. for the pagination, i use material UI component.

but there is a problem, which is when i change the page number, i have to click twice on it to appear the data in the table.

i use useState and setState will execute and we have to see rerender, but it doesn’t. all codes is in App.jsx.

even i cant use next and previous button in pagination component.

function App() {
  const [users, setUsers] = useState([]);
  const [slicedUsers, setSlicedUsers] = useState([]);

  const countPerPage = 5;
  const [currentPage, setCurrentPage] = useState(1);

  const [firstIndex, setFirstIndex] = useState(0);
  const [lastIndex, setLastIndex] = useState(countPerPage);

  const [allPageCount, setAllPageCount] = useState(0);

  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((res) => res.json())
      .then((users) => {
        setUsers(users);
        setSlicedUsers(users.slice(firstIndex, lastIndex));
        setAllPageCount(Math.floor(users.length / countPerPage));
        setIsLoading(false);
      });
  }, []);

  const paginationController = (e, value) => {
    setCurrentPage(value);
    
    setLastIndex(currentPage * countPerPage);
    setFirstIndex(lastIndex - countPerPage);
    
    setSlicedUsers(users.slice(firstIndex, lastIndex));
  };

  return (
    <>
      <Container
        sx={{
          display: "flex",
          flexDirection: "column",
          gap: "10px",
          height: "100svh",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        {isLoading ? (
          <>
            <CircularProgress color="warning" />
          </>
        ) : (
          <>
            <TableContainer
              component={Paper}
              sx={{ boxShadow: "0 0 12px #15544b" }}
            >
              <Table>
                <TableHead>
                  <TableCell>ID</TableCell>
                  <TableCell>Title</TableCell>
                  <TableCell>Body</TableCell>
                </TableHead>

                <TableBody>
                  {slicedUsers &&
                    slicedUsers.map((user) => (
                      <TableRow key={user.id}>
                        <TableCell>{user.id}</TableCell>
                        <TableCell>{user.title}</TableCell>
                        <TableCell>{user.body}</TableCell>
                      </TableRow>
                    ))}
                </TableBody>
              </Table>
            </TableContainer>

            <Pagination
              count={allPageCount}
              color="primary"
              hideNextButton
              hidePrevButton
              onChange={paginationController}
            />
          </>
        )}
      </Container>
    </>
  );
}```



2

Answers


  1. That’s because firstIndex and lastIndex are not initialized with React.useState, therefore the component doesn’t rerender. You can’t do:

    lastIndex = currentPage * countPerPage;
    firstIndex = lastIndex - countPerPage;
    

    Your usage of currentPage is correct, so do the same. Change the syntax to the following:

    const [firstIndex, setFirstIndex] = React.useState(0);
    const [lastIndex, setLastIndex] = React.useState(0);
    . . .
    setFirstIndex(. . .);
    setLastIndex(. . .);
    

    Full working code:

    import * as React from "react";
    import "./styles.css";
    
    const countPerPage = 5;
    export default () => {
      const [users, setUsers] = React.useState([]);
      const [slicedUsers, setSlicedUsers] = React.useState([]);
    
      const [currentPage, setCurrentPage] = React.useState(1);
    
      const [firstIndex, setFirstIndex] = React.useState(0);
      const [lastIndex, setLastIndex] = React.useState(countPerPage);
    
      const [allPageCount, setAllPageCount] = React.useState(0);
    
      const [isLoading, setIsLoading] = React.useState(true);
    
      React.useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/posts")
          .then((res) => res.json())
          .then((users) => {
            setUsers(users);
            setLastIndex(currentPage * countPerPage);
            setFirstIndex(lastIndex - countPerPage);
            setSlicedUsers(users.slice(firstIndex, lastIndex));
            setAllPageCount(Math.floor(users.length / countPerPage));
            setIsLoading(false);
          });
      }, []);
    
      const paginationController = (e, value) => {
        setCurrentPage(value);
        setLastIndex(currentPage * countPerPage);
        setFirstIndex(currentPage * countPerPage - countPerPage);
        setSlicedUsers(users.slice(firstIndex, lastIndex));
      };
    
      return (
        <>
          <div>users:{users.length}</div>
          <div>slicedUsers:{slicedUsers.length}</div>
          <div>currentPage:{currentPage}</div>
          <div>lastIndex:{lastIndex}</div>
          <div>firstIndex:{firstIndex}</div>
          <button onClick={() => setFirstIndex((prev) => prev + 1)}>
            paginate...
          </button>
        </>
      );
    };
    

    77830531-6whnrl

    Login or Signup to reply.
  2. To add to the excellent answer of Mike K:
    you can also utilize useEffect to make changes in currentPage and re-slice the users array whenever the page changes:

    useEffect(() => {
      const newFirstIndex = (currentPage - 1) * countPerPage;
      const newLastIndex = currentPage * countPerPage;
    
      setSlicedUsers(users.slice(newFirstIndex, newLastIndex));
    }, [currentPage, users, countPerPage]);
    

    by adding another useEffect hook, the currentPage state change triggers the effect, and handles slicing the data accordingly.

    doing this we have to remove the direct calls to setSlicedUsers from both paginationController and the fetch effect to avoid duplicates.

    the whole code:

    import React, { useState, useEffect } from "react";
    import {
      Container,
      CircularProgress,
      TableContainer,
      Paper,
      Table,
      TableHead,
      TableCell,
      TableBody,
      TableRow,
      Pagination
    } from '@mui/material';
    
    function App() {
      const [users, setUsers] = useState([]);
      const [slicedUsers, setSlicedUsers] = useState([]);
      const countPerPage = 5;
      const [currentPage, setCurrentPage] = useState(1);
      const [allPageCount, setAllPageCount] = useState(0);
      const [isLoading, setIsLoading] = useState(true);
    
      useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/posts")
          .then((res) => res.json())
          .then((data) => {
            setUsers(data);
            setAllPageCount(Math.ceil(data.length / countPerPage));
            setIsLoading(false);
          });
      }, []);
    
      useEffect(() => {
        const newFirstIndex = (currentPage - 1) * countPerPage;
        const newLastIndex = currentPage * countPerPage;
        setSlicedUsers(users.slice(newFirstIndex, newLastIndex));
      }, [currentPage, users, countPerPage]);
    
      const paginationController = (e, value) => {
        setCurrentPage(value);
      };
    
      return (
        <Container
          sx={{
            display: "flex",
            flexDirection: "column",
            gap: "10px",
            height: "100svh",
            justifyContent: "center",
            alignItems: "center",
          }}
        >
          {isLoading ? (
            <CircularProgress color="warning" />
          ) : (
            <>
              <TableContainer
                component={Paper}
                sx={{ boxShadow: "0 0 12px #15544b" }}
              >
                <Table>
                  <TableHead>
                    <TableRow>
                      <TableCell>ID</TableCell>
                      <TableCell>Title</TableCell>
                      <TableCell>Body</TableCell>
                    </TableRow>
                  </TableHead>
                  <TableBody>
                    {slicedUsers.map((user) => (
                      <TableRow key={user.id}>
                        <TableCell>{user.id}</TableCell>
                        <TableCell>{user.title}</TableCell>
                        <TableCell>{user.body}</TableCell>
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </TableContainer>
              <Pagination
                count={allPageCount}
                color="primary"
                onChange={paginationController}
              />
            </>
          )}
        </Container>
      );
    }
    
    export default App;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search