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
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
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):src/components/Clientcomponent.js
:src/app/api/route/route.js
: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-pagesrc/app/page.js
you can also importfs
or other modules that can only be used server-side.