0

The network preview whenever i refresh the page will always show duplicate api call (ipaddress)/(portnumber)/admin/user-group?page=1&page_size=10&query= twice. I have tried modifying the useEffect(), handSearch() but to no avail. I'm confident that its because of the use effect() but im having a hard time understanding why. I dont think my other component has any trouble but just in case anyone decided to take a look i'll post one after.

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import SearchBar from './searchTable';
import Pagination from './paginationTable';

interface RowData {
  [key: string]: any;
}

interface DynamicTableProps {
  columns: string[];
}

const DynamicTable: React.FC<DynamicTableProps> = ({ columns }) => {
  const itemsPerPage = 10;
  const [currentPage, setCurrentPage] = useState(1);
  const [searchValue, setSearchValue] = useState('');
  const [filteredRows, setFilteredRows] = useState<RowData[]>([]);
  const [isLoading, setIsLoading] = useState(false);

  interface UserGroupRow {
    id: number;
    name: string;
    email: string;
    phone: string;
    count: number;
  }

  const [totalItems, setTotalItems] = useState(0);

  const adminUrl = process.env.NEXT_PUBLIC_ADMIN_URL || '';

  const fetchData = async (query: string, page: number, pageSize: number) => {
    try {
      const token = localStorage.getItem('token');
      const headers = {
        'token': token ? token : '',
        'Content-Type': 'application/json',
      };
      setIsLoading(true);
      const response = await axios.get(`${adminUrl}/admin/user-group?page=${page}&page_size=${pageSize}&query=${query}`, { headers: headers });

      if (Array.isArray(response.data.data.user_groups)) {
        let formattedRows = response.data.data.user_groups.map((row: UserGroupRow) => {
          return {
            id: row.id,
            name: row.name,
            email: row.email,
            phone: row.phone,
            count: row.count,
          };
        });

        setFilteredRows(formattedRows);
        setTotalItems(response.data.meta.total);
      } else {
        setFilteredRows([]);
      }
    } catch (error) {
      console.error('Error fetching data:', error);
    } finally {
      setIsLoading(false);
    }
  };

  const fetchDataWrapper = () => {
    if (searchValue === '') {
      fetchData('', currentPage, itemsPerPage);
    } else {
      fetchData(searchValue, currentPage, itemsPerPage);
    }
  };

  useEffect(() => {
    fetchDataWrapper();
  }, [searchValue, currentPage]);

  const handleSearch = (searchValue: string) => {
    setSearchValue(searchValue);
    setCurrentPage(1);
  };

  const handlePageChange = (newPage: number) => {
    setCurrentPage(newPage);
  };

  return (
    <div className="bg-white overflow-hidden shadow-md rounded-md">
      <div className="flex items-center justify-end px-6 py-3">
        <SearchBar onSearch={handleSearch} />
      </div>
      <table className="min-w-full divide-y divide-gray-200">
        <thead className="bg-gray-50">
          <tr>
            {columns.map((column) => (
              <th
                key={column}
                className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
              >
                {column}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="bg-white divide-y divide-gray-200">
          {filteredRows.map((row, index) => (
            <tr key={index}>
              {columns.map((column) => (
                <td key={column} className="px-6 py-4 whitespace-nowrap">
                  <div className="text-sm text-gray-900">
                    {row[column] ? row[column] : `No data for ${column}`}
                  </div>
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <Pagination
        currentPage={currentPage}
        totalItems={totalItems}
        itemsPerPage={itemsPerPage}
        onPageChange={handlePageChange}
      />
    </div>
  );
};

export default DynamicTable;

UPDATE : I separate it into another component to debug the problem, heres what i did. The original problem no matter what i did to modify the useEffect() it will always cause the API to be called twice

import { useState, useEffect } from 'react';
import axios from 'axios';

interface FetchDataOptions {
  adminUrl: string;
  token: string | null;
  query: string;
  page: number;
  pageSize: number;
}

export const useFetchData = ({
  adminUrl,
  token,
  query,
  page,
  pageSize,
}: FetchDataOptions) => {
  const [data, setData] = useState<any[]>([]);
  const [totalItems, setTotalItems] = useState(0);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    if (!token) return;

    const fetchData = async () => {
      try {
        const headers = {
          token,
          'Content-Type': 'application/json',
        };
        setIsLoading(true);
        const response = await axios.get(
          `${adminUrl}/admin/user-group?page=${page}&page_size=${pageSize}&query=${query}`,
          { headers }
        );

        if (Array.isArray(response.data.data.user_groups)) {
          setData(response.data.data.user_groups);
          setTotalItems(response.data.meta.total);
        } else {
          setData([]);
        }
      } catch (error) {
        console.error('Error fetching data:', error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, [adminUrl, token, query, page, pageSize]);

  return { data, totalItems, isLoading };
};

  • Go to your index.ts or index.js where you render the website to the root element and post the code. – Dimitar Apr 28 '23 at 07:10
  • I think this issue comes from the pagination component, If you share that component also, that will be good to debug this issue. – Bennison J Apr 28 '23 at 07:33
  • I am 90% sure he is running in strict mode which will cause double execution of all effects. – Dimitar Apr 28 '23 at 07:49

1 Answers1

0

Check the dependencies ot useEffect...

useEffect(() => {
  fetchDataWrapper();
}, [searchValue, currentPage]);

const handleSearch = (searchValue: string) => {
  setSearchValue(searchValue);
  setCurrentPage(1);
};

The API is called twice because the 2 states on which useEffect depends on are set in handleSearch.

It looks like the only needed dependency is currentPage.

useEffect(() => {
  fetchDataWrapper();
}, [currentPage]);

const handleSearch = (searchValue: string) => {
  setSearchValue(searchValue);
  setCurrentPage(1);
};
Louys Patrice Bessette
  • 33,375
  • 6
  • 36
  • 64
  • I managed to fix the duplicate API by separating it into a different component for fetching data, let me update the post – Ilyas Nawawie Apr 28 '23 at 08:16