skip to Main Content

I am new in React and Next 13, I have a dropdown with some values mapped from the API and a table where data is also fetched from a different api. I want the selected project value of the dropdown to display content of the table. I want to set a condition wrapping the table. Whenever I try to use useState and ‘use client’ it is giving an error from the schema about fs and if i pass onChange in the select it’s complaining that I am using use client in a server side component. I am just stuck.

import { fetchJson } from '@/lib/util/fetchJson';
import { Dashboard, dashboard, ProjectServices } from '@/schema/api';

export default async function DashboardContentSection({ path }: { path: string }) {
  const dashboardData = await fetchJson<Dashboard>(`/${path}/data.json`);
  const projectsData = await fetchJson<ProjectServices>(`/deploy.json`);

  return (
    <div>
        <select className='w-full p-2'>
          {projectsData.apps.map((project, index) => (
            <option key={index} value={project}>
              {project}
            </option>
          ))}
        </select>
        <table className='w-full text-left'>
          <thead className='bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400'>
            <tr>
              <th scope='col' className='px-6 py-3'>
                Cluster
              </th>
              <th scope='col' className='px-6 py-3'>
                Project
              </th>
              <th scope='col' className='px-6 py-3'>
                Version
              </th>
              <th scope='col' className='px-6 py-3'>
                Traffic
              </th>
            </tr>
          </thead>
          <tbody>
            {dashboard.parse(dashboardData).data.map((i) => (
              <>
                <tr
                  className='cursor-pointer border-b bg-white hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-600'
                  key={i.cluster}
                >
                  <td className='px-6 py-3'>{i.cluster}</td>
                  <td className='px-6 py-3'>{i.project}</td>
                  {i.version[0] && (
                    <>
                      <td key={i.version[0].version} className='px-6 py-3'>
                        {i.version[0].version}
                      </td>
                      <td className='px-6 py-3'>{i.version[0].percentage}%</td>
                    </>
                  )}
                </tr>
              </>
            ))}
          </tbody>
        </table>
    </div>
  );
}

here is the fetchJson.ts file structure

import { FileApiRoutes } from '@/types/ApiRoutes';
import fs from 'fs';
import path from 'path';

export const fetchJson = <T>(fileRoute: FileApiRoutes): Promise<T> => {
  // Determine the path to the JSON file
  const jsonFilePath = path.resolve('./src/app/' + fileRoute);

  // Read the JSON file
  const jsonData = fs.readFileSync(jsonFilePath, 'utf-8');

  // Parse the JSON data
  const data: T = JSON.parse(jsonData);

  // Return the data as a resolved Promise
  return Promise.resolve(data);
};

2

Answers


  1. You can’t use fs in a client-side component. I would recommend creating an API route for example /api/data?query=your_data_query then fetch the data on the client-side component.

    https://nextjs.org/docs/pages/building-your-application/routing/api-routes

    Login or Signup to reply.
  2. The fetch call could originate from a server page which passes the data to a client component. However if it’s likely that the data will change at short intervals, I would recommend using an API route which can be fetched again from the client component dynamically. I’ve created a functional example.

    src/app/page.js (server-page):

    import Clientcomponent from "@/components/Clientcomponent";
    
    export default function Home() {
        return (
            <main>
                <Clientcomponent />
            </main>
        );
    }
    

    src/components/Clientcomponent.js:

    "use client";
    
    import { useEffect, useState } from "react";
    
    export default function Clientcomponent() {
      const [tableContent, setTableContent] = useState(null);
      const [tableName, setTableName] = useState("table1");
    
      useEffect(() => {
        fetch(`/api/route?tableName=${tableName}`)
          .then((res) => res.json())
          .then((data) => setTableContent(data));
      }, tableName);
      return (
        <div>
          {
            <select
              value={tableName}
              onChange={(event) => {
                setTableName(event.target.value);
              }}>
              <option value="table1">table1</option>
              <option value="table2">table2</option>
              <option value="table3">table3</option>
            </select>
          }
          <table>
            <thead>
              <tr>
                <th>first column</th>
                <th>second column</th>
                <th>third column</th>
              </tr>
            </thead>
            <tbody>
              {tableContent
                ? tableContent.map((array, index) => (
                  <tr key={index}>
                    {array.map((value, index) => (
                      <td key={index}>{value}</td>
                    ))}
                  </tr>
                ))
              : null}
            </tbody>
          </table>
        </div>
      );
    }
    

    src/app/api/route/route.js:

    import { NextResponse } from "next/server";
    
    const data = {
      table1: [
        ["1.1.1", "1.1.2", "1.1.3"],
        ["1.2.1", "1.2.2", "1.2.3"],
        ["1.3.1", "1.3.2", "1.3.3"]
      ],
      table2: [
        ["2.1.1", "2.1.2", "2.1.3"],
        ["2.2.1", "2.2.2", "2.2.3"],
        ["2.3.1", "2.3.2", "2.3.3"]
      ],
      table3: [
        ["3.1.1", "3.1.2", "3.1.3"],
        ["3.2.1", "3.2.2", "3.2.3"],
        ["3.3.1", "3.3.2", "3.3.3"]
      ]
    };
    
    export async function GET(request) {
      const url = new URL(request.url);
      const tableName = url.searchParams.get("tableName");
      return new NextResponse(JSON.stringify(data[tableName]));
    }
    

    Of course the data can be fetched from an external resource from route.js.
    From src/app/api/route/route.js or from the server-page src/app/page.js you can also import fs or other modules that can only be used server-side.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search