Warrior
Warrior

Reputation: 63

useEffect not running in nextJS 13.4 (app router) app

i am trying to fetch data from next api route but the useEffect method is not firing up on page reload. also i can't make this component async as nextJS does'nt allow async functions on client components(use client)

page.tsx:

"use client";
import { useEffect, useState } from "react";
import DataTable from "./components/table";
import { Resizable } from "re-resizable";

const SamplePage: React.FC = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch("/api/data")
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        console.log("data");
      });
  }, []);

  console.log(data, "dataaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

  const headers = [
    "Timestamp",
    "Purchase Id",
    "Mail",
    "Name",
    "Source",
    "Status",
    "Select",
  ];
  const rows = [
    {
      Timestamp: "2023-07-15",
      "Purchase Id": "P001",
      Mail: "[email protected]",
      Name: "John Doe",
      Source: "Web",
      Status: "Completed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-16",
      "Purchase Id": "P002",
      Mail: "[email protected]",
      Name: "Jane Smith",
      Source: "Mobile",
      Status: "Waiting",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-17",
      "Purchase Id": "P003",
      Mail: "[email protected]",
      Name: "Mike Johnson",
      Source: "Web",
      Status: "Failed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-18",
      "Purchase Id": "P004",
      Mail: "[email protected]",
      Name: "Emily Brown",
      Source: "Web",
      Status: "Completed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-19",
      "Purchase Id": "P005",
      Mail: "[email protected]",
      Name: "David Wilson",
      Source: "Mobile",
      Status: "Waiting",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-20",
      "Purchase Id": "P006",
      Mail: "[email protected]",
      Name: "Sarah Davis",
      Source: "Web",
      Status: "Failed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-21",
      "Purchase Id": "P007",
      Mail: "[email protected]",
      Name: "Alex Johnson",
      Source: "Mobile",
      Status: "Completed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-22",
      "Purchase Id": "P008",
      Mail: "[email protected]",
      Name: "Emma Smith",
      Source: "Web",
      Status: "Waiting",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-23",
      "Purchase Id": "P009",
      Mail: "[email protected]",
      Name: "James Wilson",
      Source: "Web",
      Status: "Failed",
      Select: "Select",
    },
    {
      Timestamp: "2023-07-24",
      "Purchase Id": "P010",
      Mail: "[email protected]",
      Name: "Olivia Brown",
      Source: "Mobile",
      Status: "Completed",
      Select: "Select",
    },
  ];
  const [state, setState] = useState({ width: "95%", height: "80%" });
  return (
    <>
      <Resizable
        style={{ border: "1px solid black" }}
        size={{ width: state.width, height: state.height }}
        onResizeStop={(e, direction, ref, d) => {
          setState({
            width: state.width + d.width,
            height: state.height + d.height,
          });
        }}
      >
        <DataTable
          headers={headers}
          rows={rows}
          caption="Bookings Details (pagination,sorting added)"
          sortable
          pagination
          colorScheme="teal"
        />
      </Resizable>
    </>
  );
};

export default SamplePage;

DataTable.tsx

"use client"
import {
  Table,
  Thead,
  Tbody,
  Tr,
  Th,
  Td,
  Box,
  Input,
  Flex,
} from "@chakra-ui/react";
import { useState } from "react";
import styles from "./table.module.css";
import { Button, ButtonGroup } from "@chakra-ui/react";
import {
  ChevronRightIcon,
  ChevronLeftIcon,
  SearchIcon,
  ChevronDownIcon,
  ChevronUpIcon,
} from "@chakra-ui/icons";
import dynamic from "next/dynamic";

interface DataTableProps {
  headers: string[];
  rows: Record<string, string | number>[];
  caption?: string;
  sortable?: boolean;
  pagination?: boolean;
  itemsPerPage?: number;
  tableStyling?: string;
  colorScheme?: string;
}

const DataTable: React.FC<DataTableProps> = ({
  headers,
  rows,
  caption,
  sortable,
  pagination,
  itemsPerPage,
  tableStyling,
  colorScheme,
}) => {
  const [sortConfig, setSortConfig] = useState<{
    column: string;
    ascending: boolean;
  }>({
    column: "",
    ascending: true,
  });
  const [currentPage, setCurrentPage] = useState(1);
  const [perPage, setPerPage] = useState(itemsPerPage || 5);
  const [searchTerms, setSearchTerms] = useState<{ [column: string]: string }>(
    {}
  );
  const [resizableColumns, setResizableColumns] = useState<string[]>([]);

  const handleHeaderClick = (column: string) => {
    if (!sortable) return;

    if (sortConfig.column === column) {
      setSortConfig({ ...sortConfig, ascending: !sortConfig.ascending });
    } else {
      setSortConfig({ column, ascending: true });
    }
  };

  const sortRows = () => {
    if (!sortConfig.column) return rows;

    const sortedRows = [...rows];

    sortedRows.sort((a, b) => {
      const valueA = a[sortConfig.column];
      const valueB = b[sortConfig.column];

      if (typeof valueA === "string" && typeof valueB === "string") {
        return sortConfig.ascending
          ? valueA.localeCompare(valueB)
          : valueB.localeCompare(valueA);
      } else {
        return sortConfig.ascending
          ? (valueA as number) - (valueB as number)
          : (valueB as number) - (valueA as number);
      }
    });

    return sortedRows;
  };

  const filterRows = () => {
    return rows.filter((row) => {
      return Object.entries(searchTerms).every(([column, searchTerm]) => {
        const value = row[column]?.toString().toLowerCase();
        return value?.includes(searchTerm.toLowerCase());
      });
    });
  };

  const getFilteredAndSortedRows = () => {
    const filteredRows = filterRows();
    const sortedRows = sortRows();
    return sortedRows.filter((row) => filteredRows.includes(row));
  };

  const renderRows = () => {
    const sortedAndFilteredRows = getFilteredAndSortedRows();

    const startIndex = (currentPage - 1) * perPage;
    const endIndex = startIndex + perPage;

    return sortedAndFilteredRows
      .slice(startIndex, endIndex)
      .map((row, index) => (
        <Tr key={index}>
          {headers.map((header, index) => {
            return (
              <Td key={index}>
                <Box
                  className={
                    row[header] === "Completed"
                      ? `${styles.completed} ${styles.status}`
                      : row[header] === "Waiting"
                      ? `${styles.waiting} ${styles.status}`
                      : row[header] === "Failed"
                      ? `${styles.failed} ${styles.status}`
                      : row[header] === "Select"
                      ? `${styles.yes} ${styles.status}`
                      : ""
                  }
                >
                  {row[header]}
                </Box>
              </Td>
            );
          })}
        </Tr>
      ));
  };

  const renderPagination = () => {
    const totalPages = Math.ceil(filterRows().length / perPage);
    const previousDisabled = currentPage === 1;
    const nextDisabled = currentPage === totalPages;

    return (
      <div className={styles.pagination}>
        <ButtonGroup>
          <Button
            className={styles.paginationBtn}
            onClick={() =>
              setCurrentPage(currentPage === 1 ? currentPage : currentPage - 1)
            }
            disabled={previousDisabled}
            leftIcon={<ChevronLeftIcon fontSize={"2rem"} color={"white"} />}
          ></Button>
          <Button
            className={styles.paginationBtn}
            onClick={() =>
              setCurrentPage(
                currentPage === totalPages ? currentPage : currentPage + 1
              )
            }
            disabled={nextDisabled}
            rightIcon={<ChevronRightIcon fontSize={"2rem"} color={"white"} />}
          ></Button>
        </ButtonGroup>
        <span className={styles.currentPage}>
          Page {currentPage} of {totalPages}
        </span>
        <span className={styles.itemsPerPage}>
          Items per page:
          <select
            value={perPage}
            onChange={(e) => {
              setCurrentPage(1);
              setPerPage(parseInt(e.target.value));
            }}
          >
            <option value={5}>5</option>
            <option value={10}>10</option>
            <option value={15}>15</option>
          </select>
        </span>
      </div>
    );
  };

  return (
    <Table
      className={styles.tableStyle}
      size={"sm"}
      variant={tableStyling}
      colorScheme={colorScheme}
    >
      {caption && (
        <caption>
          <div
            style={{ marginBottom: "2rem", fontWeight: "bold" }}
            className="captionStyle"
          >
            {caption}
          </div>
        </caption>
      )}
      <Thead>
        <Tr>
          {headers.map((header, index) => (
            <Th
              key={index}
              onClick={() => handleHeaderClick(header)}
              cursor={sortable ? "pointer" : "default"}
            >
              {header}
            </Th>
          ))}
        </Tr>
        <Tr>
          {headers.map((header, index) => (
            <Td key={index}>
              <Flex align="center">
                <Input
                  size="sm"
                  variant="outline"
                  placeholder={`Search ${header}`}
                  value={searchTerms[header] || ""}
                  onChange={(e) =>
                    setSearchTerms((prevSearchTerms) => ({
                      ...prevSearchTerms,
                      [header]: e.target.value,
                    }))
                  }
                  pr="2.5rem"
                />
                {/* <SearchIcon
                  boxSize={4}
                  color="gray.400"
                  position="absolute"
                  right="0.75rem"
                /> */}
              </Flex>
            </Td>
          ))}
        </Tr>
      </Thead>
      <Tbody>{renderRows()}</Tbody>
      {pagination && renderPagination()}
    </Table>
  );
};

export default DataTable;

tried creating different utils.ts file for fetching data but that does'nt work either

Upvotes: 3

Views: 1607

Answers (2)

seungdo
seungdo

Reputation: 11

Check out Node version.(16.14 or later)

Upvotes: 1

Philipe Almeida
Philipe Almeida

Reputation: 41

Possibly the cache is turned on and preventing a new request from being made.

Try to change your fetch function in your correspondent /api file, to use no-store (no cache) as recommended in next.js documentation option, to fetches the resource from the remote server on every request without looking in the cache.


fetch(`https://...`, { cache: 'no-store' })

Hope this helps!

Upvotes: 0

Related Questions