Search code examples
javascriptreactjsnext.jsaxiostanstack

Next.js fetched data comes in, but I can't get it to display on the page


I can get the data to show up in a console.log from the first file, where it is being grabbed by axios, but can't get it to display in my tanstack table or just as a string inside the HTML. I have tested my tanstack table with hardcoded data and it worked great. Included is an image of the console, with the data showing, between help1 and help2, but not between dell1 and dell2.

File ProjectsTableBlock:

import React, { ReactNode, useEffect } from "react";
import Block from "./Block";
import Button from "../Button";
import Table from "../Table";

import { createColumnHelper } from "@tanstack/react-table";
import axios from "axios";

type ProjectsTableBlockProps = {
    block_span: string;
    children: ReactNode;
};

type Project = {
    project_id: number;
    name: string;
    description: string;
    organization_id: number;
};

const columnHelper = createColumnHelper<Project>();

const columns = [
    columnHelper.accessor("project_id", {
        header: () => "Project ID",
        cell: (info) => info.getValue(),
        footer: (info) => info.column.id,
    }),
    columnHelper.accessor("name", {
        cell: (info) => <i>{info.getValue()}</i>,
        header: () => "Name",
        footer: (info) => info.column.id,
    }),
    columnHelper.accessor("description", {
        header: "Description",
        cell: (info) => info.getValue(),
        footer: (info) => info.column.id,
    }),
    columnHelper.accessor("organization_id", {
        header: "Organization ID",
        cell: (info) => info.getValue(),
        footer: (info) => info.column.id,
    }),
];

export default function ProjectsTableBlock(ProjectsTableBlockProps) {
    const [data, setData] = React.useState<Project[]>([]);

    useEffect(() => {
        axios
            .get("/api/projects")
            .then((response) => {
                setData(response.data);
            })
            .catch((error) => console.error("Error fetching projects:", error));
    }, []);

    console.log("help1");
    console.log(data);
    console.log("help2");

    return (
        <Block>
            <div className=" flex items-center justify-between pb-6">
                <div>
                    <h2 className="text-gray-600 font-semibold">Projects</h2>
                    <span className="text-xs">Projects List</span>
                </div>
                <div className="flex items-center justify-between">
                    <div className="lg:ml-40 ml-10 space-x-8">
                        <Button action="Create">Create</Button>
                    </div>
                </div>
            </div>
            <div>
                <div className="-mx-4 sm:-mx-8 px-4 sm:px-8 py-4 overflow-x-auto">
                    <div className="inline-block min-w-full shadow rounded-lg overflow-hidden">
                        <Table columns={columns} data={data}></Table>
                    </div>
                </div>
            </div>
        </Block>
    );
}

File Table.tsx

import { ViewColumnsIcon } from "@heroicons/react/24/outline";
import React, { useState } from "react";

import {
    createColumnHelper,
    flexRender,
    getCoreRowModel,
    useReactTable,
} from "@tanstack/react-table";

type TableProps = {
    columns: any;
    data: any;
};

export default function Table(TableProps) {
    const [data, setData] = React.useState(() => [...TableProps.data]);

    const table = useReactTable({
        data,
        columns: TableProps.columns,
        getCoreRowModel: getCoreRowModel(),
    });

    console.log("dell1");
    console.log(data);
    console.log("dell2");

    return (
        <div>
            <table className="min-w-full">
                <thead className="bg-white border-b">
                    {table.getHeaderGroups().map((headerGroup) => (
                        <tr key={headerGroup.id}>
                            {headerGroup.headers.map((header) => (
                                <th key={header.id}>
                                    {header.isPlaceholder
                                        ? null
                                        : flexRender(
                                              header.column.columnDef.header,
                                              header.getContext()
                                          )}
                                </th>
                            ))}
                        </tr>
                    ))}
                </thead>
                <tbody>
                    {table.getRowModel().rows.map((row) => (
                        <tr className="bg-white border-b" key={row.id}>
                            {row.getVisibleCells().map((cell) => (
                                <td
                                    className="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"
                                    key={cell.id}
                                >
                                    {flexRender(
                                        cell.column.columnDef.cell,
                                        cell.getContext()
                                    )}
                                </td>
                            ))}
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    );
}

I expected the data to get displayed in the tanstack table or at least show up in the console log.


Solution

  • Issue

    You declared an empty data array state in ProjectsTableBlock, which gets updated after the data is fetched via axios. But you gave that initial empty array to Table as part of props and used it to create an internal state called data. And the thing about useState, is that the initial state can only be updated by calling setState.

    Solution

    One way to fix your issue is to remove the inner state, and use the props, like so:

    export default function Table(TableProps) {
      // const [data, setData] = React.useState(() => [...TableProps.data]); 👈🏽
    
      const table = useReactTable({
          // 👇🏽
          data: TableProps.data, 
          columns: TableProps.columns,
          getCoreRowModel: getCoreRowModel(),
      });
    
      console.log("dell1");
      console.log(TableProps.data);
      console.log("dell2");
    
      //...
    
    }
    

    Or, if you need to manipulate that inner data state, make sure inside ProjectsTableBlock that you only render Table after the data is fully fetched, like so:

    {data.length > 0 && <Table columns={columns} data={data}></Table>}  
    

    Very often, you would wanna show a loader in place:

    {data.length > 0 ? <Table columns={columns} data={data}></Table> : "Loading..."}