0

Pagination issue with fetching data in React using useState and useEffect

I'm working on a React application where I have a UserList component that displays a list of users fetched from a backend API. I'm implementing a pagination system using the useState and useEffect hooks, and I'm facing an issue with fetching the data based on the selected page number and page size.

Here's a simplified version of my code:

import { Link, useNavigate } from 'react-router-dom';
import React, { useEffect, useState } from 'react';
import UiContent from "../../../Components/Common/UiContent";
import { Button, ButtonGroup, Card, CardBody, Col, Container, Row } from "reactstrap";
import BreadCrumb from "../../../Components/Common/BreadCrumb";
import { _, Grid } from "gridjs-react";
import userService from "../../../helpers/user.service";

const UserList = () => {
    const navigate = useNavigate();
    const [state, setState] = useState({ users: [], isLoading: true, currentPage: 0, pageSize: 5, totalRecords: 0 });

    useEffect(() => {
        fetchUserList(state.currentPage, state.pageSize);
    }, []);

    const fetchUserList = async (page, limit) => {
        try {
            const response = await userService.getUserList(page, limit);
            console.log(response);
            const { userDtoList, totalRecords } = response.data;
            setState(prevState => ({ ...prevState, users: userDtoList, totalRecords, isLoading: false }));
        } catch (error) {
            console.log(error);
        }
    };

    const removeUser = async (id) => {
        try {
            await userService.deleteUser(id);
            setState(prevState => ({
                ...prevState,
                users: prevState.users.filter(user => user.id !== id),
                isLoading: false
            }));
        } catch (error) {
            console.log(error);
        }
    };

    const editUser = (id) => {
        navigate(`/users/${id}`);
    };

    const onPageChange = (pageNumber) => {
        setState(prevState => ({ ...prevState, currentPage: pageNumber }));
        fetchUserList(pageNumber, state.pageSize);
    };

    const onPageSizeChange = (pageSize) => {
        setState(prevState => ({ ...prevState, pageSize, currentPage: 1 }));
        fetchUserList(1, pageSize);
    };

    const { users, isLoading, currentPage, pageSize, totalRecords } = state;

    if (isLoading) {
        return <p>Loading...</p>;
    }
    console.log(users);
    console.log(totalRecords);

    if (!users) {
        return <p>No users found.</p>;
    }

    const gridColumns = [
        {
            name: 'ID',
            formatter: (cell) => _(<span className="fw-semibold">{cell}</span>)
        },
        "firstName",
        "lastName",
        {
            name: 'Email',
            formatter: (cell) => _(<a href="/#"> {cell} </a>)
        },
        {
            name: 'Actions',
            width: '120px',
            formatter: (cell, row) => _(
                <div>
                    <ButtonGroup>
                        <Button
                            size="sm"
                            color="primary"
                            onClick={() => editUser(row.cells[0].data)}>
                            Edit
                        </Button>
                        <Button
                            size="sm"
                            color="danger"
                            onClick={() => removeUser(row.cells[0].data)}>
                            Delete
                        </Button>
                    </ButtonGroup>
                </div>
            )
        },
    ];

    const gridData = users ? users.map(user => [
        user.id,
        user.firstName,
        user.lastName,
        user.email,
    ]) : [];

    return (
        <React.Fragment>
            <UiContent />
            <div className="page-content">
                <Container fluid>
                    <BreadCrumb title="List" pageTitle="Users" />
                    <Row>
                        <Col lg={12}>
                            <Card>
                                <Link to="/users/new">
                                    <Button color="success" title="Add Users">
                                        Add User
                                    </Button>
                                </Link>
                                <CardBody>
                                    <div id="table-gridjs">
                                        <Grid
                                            columns={gridColumns}
                                            /*data={gridData}*/
                                               server= {{
                                                   url: 'http://localhost:8081/api/user/list/0/5',
                                                   then: data => data.userDtoList,
                                                   total: data => data.totalRecords
                                               }}
                                            search={true}
                                            sort={true}
                                            fixedHeader={true}

                                            pagination={{
                                                enabled: true,
                                                limit: pageSize,
                                                onChange: onPageChange,
                                                onPageSizeChange: onPageSizeChange,
                                                total: totalRecords,
                                                current: currentPage,
                                                  server: {
                                                      url: (prev, page, limit) => `${prev}?limit=${limit}&offset=${page}`
                                              }
                                            }}
                                        />
                                    </div>
                                </CardBody>
                            </Card>
                        </Col>
                    </Row>
                </Container>
            </div>
        </React.Fragment>
    );
};

export default UserList;

The issue I'm facing is that when I change the page or page size, the data is not being fetched correctly. The initial data is fetched correctly on component mount, but subsequent requests don't update the user list.

I suspect that there might be an issue with the useEffect dependency array or the way I'm updating the state in the fetchUserList function.

I've tried debugging the code and logging the values of currentPage, pageSize, and totalRecords, and they seem to be updated correctly when interacting with the pagination component. However, the fetchUserList function is not fetching the correct data.

It's worth noting that when I use the server prop of Grid.js, the pagination works correctly. However, in my fetchUserList function, I need to include a token for authentication purposes, which is why I want to send the request from the userService module.

Can someone please guide me on how to properly implement the pagination system using the useState and useEffect hooks, while also incorporating the token authentication in the fetchUserList function? Specifically, how can I ensure that the data is fetched correctly when the page number or page size changes, and how can I include the token in the request?

1 Answers1

-1

It seems like the issue is indeed in how the useState and useEffect hooks are being used. Your suspicion about the useEffect dependency array is correct. When you update the state using setState, React schedules a re-render. However, in your case, the async fetchUserList function is not always using the latest state.

You've called fetchUserList in two places: Inside useEffect hook where you're using state.currentPage and state.pageSize which are initialized as 0 and 5, respectively. Inside onPageChange and onPageSizeChange handlers where you're using a new page number and size directly.

In the second case, the state is updated first and then fetchUserList is called with new arguments. This is the correct approach. However, in the first case, fetchUserList is not called with updated state. This is due to the missing dependencies in the useEffect dependency array.

To make sure fetchUserList is called every time state.currentPage or state.pageSize changes, you should include them in the dependency array of useEffect. You should also move the function fetchUserList outside the useEffect as it's being used elsewhere in your component.

Here's how your updated code should look like:

const UserList = () => {
    // ...
    // Rest of your code

    const fetchUserList = async (page, limit) => {
        // ...
        // Rest of your code
    };

    useEffect(() => {
        fetchUserList(state.currentPage, state.pageSize);
    }, [state.currentPage, state.pageSize]); // Add state.currentPage and state.pageSize as dependencies

    // Rest of your code
};

Also, one more thing: in onPageSizeChange, you're setting currentPage to 1. If you're using a zero-based page index, it should be 0.

const onPageSizeChange = (pageSize) => {
    setState(prevState => ({ ...prevState, pageSize, currentPage: 0 }));
    fetchUserList(0, pageSize);
};

These changes should ensure that your fetch function is being called with the correct and latest state.

Ahmad
  • 49
  • 7