Я экспериментирую с nextjs (исходя из реакции). Я думаю, что у меня в основном все работает, но когда я создаю серверный компонент для проверки выборки на стороне сервера, я получаю странные результаты.
Большую часть времени, когда я запускаю сервер и загружаю страницу, постоянно отображается сообщение Loading.... Затем, если я обновлю страницу, данные отобразятся так, как ожидалось. При навигации по сайту и возврате на страницу обычно отображаются данные, но иногда отображается Loading, и если это происходит, мне приходится обновлять страницу, чтобы данные снова отображались.
Я подозреваю, что это как-то связано с кешированием nextjs, но я не уверен.
Мой компонент страницы выглядит примерно так, примерно украден из документации nextjs:
let statsData: StatsType[];
let url = 'stats';
async function getData() {
const res = await fetchDataServerSide(url);
if (!res.ok) {
throw new Error('Failed to fetch data')
}
await res.json().then(json => {
statsData = json;
})
}
const Dashboard = () => {
const data = getData();
/*if (error) return <div>Failed to load</div>*/
if (!statsData) {
return <div>Loading...</div>
} else {
return (
<div className = {"p-6"}>
<div className = {"pb-6"}>
<Stats stats = {statsData}/>
</div>
</div>
)
}
}
export default Dashboard
мой fetchDataServerSide был просто попыткой абстрагировать fetch от каждой страницы, которая мне нужна для извлечения данных, поскольку мне нужно каждый раз передавать заголовки для работы с моим бэкэндом API laravel:
async function fetchDataServerSide(url: string): Promise<Response> {
'use server'
let fetch_path = process.env.NEXT_PUBLIC_BACKEND_URL + '/api/' + url
const options: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json",
"Referer": process.env.APP_URL,
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json",
"cookie": "XSRF-TOKEN = " + cookies().get('XSRF-TOKEN').value + ";laravel_session = " + cookies().get('laravel_session').value,
}
}
return await fetch(fetch_path, options);
}
Есть мысли о том, что я делаю неправильно?





Вы не ждете получения данных. Вам необходимо изменить «Панель мониторинга», чтобы она стала асинхронным компонентом, и использовать границу ожидания с резервным пользовательским интерфейсом загрузки. Он будет отображать пользовательский интерфейс загрузки до тех пор, пока не будут загружены данные, необходимые дочерним элементам. Когда данные загружаются, компонент статистики отображается с данными. Компонент ожидания используется для ожидания получения данных и после получения возвращает данные дочерним элементам.
import { notFound } from "next/navigation";
import { Suspense } from 'react';
async function Await<T>({ promise, children }: { promise: Promise<T>, children: (value: T) => JSX.Element }) {
let data = await promise;
if (!data) return notFound();
return children(data);
}
const Dashboard = async () => {
const data = getData();
return (
<Suspense fallback = {<div>Loading...</div>}>
<Await promise = {data}>
{( data ) => <Stats stats = {data}/>}
</Await>
</Suspense>
);
};
Вам нужно преобразовать компонент Dashboard в асинхронный, это нормально, поскольку это серверный компонент. Затем дождитесь вашего getData() для условной визуализации вашего компонента статистики.
const data = await getData();
Затем, если вы хотите сохранить представление загрузки, вы можете создать скелет loading.js в той же папке маршрута:
export default function Loading() {
//custom loading skeleton component
return <p>Loading...</p>
}
https://nextjs.org/docs/app/api-reference/file-conventions/loading
Похоже, вы столкнулись с проблемой асинхронного поведения в компоненте Dashboard. Функция getData является асинхронной, и вы не ждете ее завершения перед отрисовкой компонента. В результате компонент отображается с неопределенным значением statsData, что приводит к появлению сообщения «Загрузка...».
Вот как вы можете это исправить:
import { useState, useEffect } from 'react';
const Dashboard = () => {
const [statsData, setStatsData] = useState<StatsType[] | null>(null);
useEffect(() => {
async function fetchData() {
try {
const res = await fetchDataServerSide(url);
if (!res.ok) {
throw new Error('Failed to fetch data');
}
const json = await res.json();
setStatsData(json);
} catch (error) {
console.error('Error fetching data:', error);
// Handle error state if needed
}
}
fetchData();
}, []); // Empty dependency array ensures this effect runs only once
if (!statsData) {
return <div>Loading...</div>;
} else {
return (
<div className = "p-6">
<div className = "pb-6">
<Stats stats = {statsData} />
</div>
</div>
);
}
};
export default Dashboard;При таком подходе:
Это должно решить проблему несогласованного отображения данных, с которой вы столкнулись.