javascriptnext.jstanstack-table

Calling functions from Main Component while using tanstack table


I have the following reusable react component:

"use client";
import { useState } from "react";
import {
    ColumnDef,
    flexRender,
    ColumnFiltersState,
    getFilteredRowModel,
    getCoreRowModel,
    getPaginationRowModel,
    useReactTable,
} from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from "@/components/ui/table";

function DataTable({
    columns,
    data,
    searchColumn,
}) {

    const [columnFilters, setColumnFilters] = useState([])
    const table = useReactTable({
        data,
        columns,
        onColumnFiltersChange: setColumnFilters,
        getFilteredRowModel: getFilteredRowModel(),
        getCoreRowModel: getCoreRowModel(),
        getPaginationRowModel: getPaginationRowModel(),
        state: {
            columnFilters: columnFilters,
        }
    });

    return (
        <>
            <div className="flex items-center py-4">
                <Input
                    placeholder="Search..."
                    value={(table.getColumn(searchColumn)?.getFilterValue()) ?? ""}
                    onChange={(event) =>
                        table.getColumn(searchColumn)?.setFilterValue(event.target.value)
                    }
                    className="max-w-sm"
                />
            </div>
            <div className="border rounded-md">
                <Table>
                    <TableHeader>
                        {table.getHeaderGroups().map((headerGroup) => (
                            <TableRow key={headerGroup.id}>
                                {headerGroup.headers.map((header) => {
                                    return (
                                        <TableHead key={header.id}>
                                            {header.isPlaceholder
                                                ? null
                                                : flexRender(
                                                    header.column.columnDef.header,
                                                    header.getContext()
                                                )}
                                        </TableHead>
                                    );
                                })}
                            </TableRow>
                        ))}
                    </TableHeader>
                    <TableBody>
                        {table.getRowModel().rows?.length ? (
                            table.getRowModel().rows.map((row) => (
                                <TableRow
                                    key={row.id}
                                    data-state={row.getIsSelected() && "selected"}
                                >
                                    {row.getVisibleCells().map((cell) => (
                                        <TableCell key={cell.id}>
                                            {flexRender(cell.column.columnDef.cell, cell.getContext())}
                                        </TableCell>
                                    ))}
                                </TableRow>
                            ))
                        ) : (
                            <TableRow>
                                <TableCell colSpan={columns.length} className="h-24 text-center">
                                    No results.
                                </TableCell>
                            </TableRow>
                        )}
                    </TableBody>
                </Table>
            </div>
            <div className="flex items-center justify-end py-4 space-x-2">
                <Button
                    variant="outline"
                    size="sm"
                    onClick={() => table.previousPage()}
                    disabled={!table.getCanPreviousPage()}
                >
                    Previous
                </Button>
                <Button
                    variant="outline"
                    size="sm"
                    onClick={() => table.nextPage()}
                    disabled={!table.getCanNextPage()}
                >
                    Next
                </Button>
            </div>
        </>
    );
}

export default DataTable;

And my main component is:

"use client";
import React, { useEffect, useState } from "react";
import DataTable from "@/components/ui/data-table";
import { columns } from "./columns";

function Customers() {
  const [data, setData] = useState([]);

  useEffect(() => {
    getAllDishes();
  }, []);

  async function getAllDishes() {
    const response = await fetch("/api/getalldishes");
    const data = await response.json();
    const usersData = data.data;
    setData(usersData);
  }

  return (
    <>
      <div className="container my-5">
        <h1 className="mb-4 text-2xl font-semibold">Dishes</h1>
        <DataTable columns={columns} data={data} searchColumn={"name"} />
      </div>
    </>
  );
}

export default Customers;

As you can see i am fetching data here and passing to the reusable component along with columns.

My columns.js:

"use client";
import Image from "next/image";
import { Switch } from "@/components/ui/switch";

async function handleCheckChange(id, updatedValue) {
  try {
    const response = await fetch("/api/admin/updateavailability", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        id,
        updatedValue,
      }),
    });
    const data = await response.json();
    if (!data.success) {
      throw data.message;
    }
    window.location.reload();
  } catch (err) {
    console.log(err);
  }
}
export const columns = [
  {
    accessorKey: "isVeg",
    header: "",
    cell: ({ row }) => (
      <div className="flex justify-center">
        {row.getValue("isVeg") ? (
          <Image
            src="https://img.icons8.com/color/480/vegetarian-food-symbol.png"
            style={{ width: "15px", height: "15px" }}
            className="inline-flex me-3"
            width={15}
            height={15}
            alt=""
          />
        ) : (
          <Image
            src="/non-veg.png"
            style={{ width: "15px", height: "15px" }}
            className="inline-flex me-3"
            width={15}
            height={15}
            alt=""
          />
        )}
      </div>
    ),
  },
  {
    accessorKey: "name",
    header: "Name",
  },
  {
    accessorKey: "description",
    header: "Description",
    cell: ({ row }) => (
      <div className="w-[150px] truncate">
        <span>{row.getValue("description")}</span>
      </div>
    ),
  },
  {
    accessorKey: "course",
    header: "Course",
  },
  {
    accessorKey: "cuisine",
    header: "Cuisine",
  },
  {
    accessorKey: "calories",
    header: "Calories",
    cell: ({ row }) => <span>{row.getValue("calories")} Kcal</span>,
  },
  {
    accessorKey: "price",
    header: "Price",
    cell: ({ row }) => <span>₹{row.getValue("price").toFixed(2)}</span>,
  },
  {
    accessorKey: "available",
    header: "Available",
    cell: ({ row }) => {
      return (
        <div className="flex justify-center">
          <Switch
            checked={row.getValue("available")}
            onClick={() =>
              handleCheckChange(row.original._id, !row.original.available)
            }
          />
        </div>
      );
    },
  },
];

I have a column called available, where i am rendering a switch and on toggling that, i am updating the db. I want to be able to refetch the new data when this happens. But the data is in my main component. How do i achieve this?


Solution

  • Move the columns inside the same main component instead of a separate file. You now have access to the functions and states. Refetch the data