我是next.js的新手(使用v13和App Router),我正在寻求关于我一直在努力解决的问题的建议。
我的目标是在页面上呈现数据,并随着“更好”的数据(例如,更新的数据,来自更多来源的数据等)逐渐更新它。通常情况下,应该有2-3个更新**,直到数据完成加载(例如,在酒店预订网站上,选项逐渐更新,直到加载指示器关闭)。我想尽可能地执行这个服务器端。
当数据需要更新时,我的API响应(Django后端)包含一个带有url的"next"
条目,例如:
{ "meta": {..., "next": "https://api.example.com/path/to/next/request"},
"data": ... }
next.js文档对顺序数据获取非常清楚,但它没有显示如何更新同一组数据,只是如何渐进地显示不同的数据块。
在尝试了不同的方法之后,我实现了一个使用递归组件的解决方案,用当前数据填充<Suspense>
边界作为fallback
。
import { Suspense } from 'react';
export default async function Stream() {
return (
<div className='m-auto h-screen w-1/2 p-3'>
<h1>Welcome to the Stream page!</h1>
<Results url={'http://127.0.0.1:3000/api/dummyData'} />
</div>
);
}
async function Results({ url }: { url?: string }) {
const content = await fetch(url, {
cache: 'no-store',
}).then((res) => res.json());
// if "next" url in response, insert in recursive function
if (content.next) {
return (
<Suspense fallback={<ResultsDisplayer content={content} />}>
<Results url={content.next} />
</Suspense>
);
} else {
return <ResultsDisplayer content={content} />;
}
}
async function ResultsDisplayer({ content }) {
const isLoading = Boolean(content.next);
return (
<>
<p>Loading: {isLoading ? 'YES' : 'NOPE'}</p>
<br />
<p>Data:</p>
<pre>{JSON.stringify(content)}</pre>;
</>
);
}
Illustration of recursive solution (GIF)
这个解决方案似乎有效,但是我很好奇是否有一种更传统/直接的方法来实现由链接超链接触发的服务器端数据变化。我找不到任何好的例子或文档来覆盖这个用例。
我在研究这个问题的同时也探索了其他的选择,但没有一个是真正合适的:
*客户端更新(例如使用useSWR和useEffect),但希望避免客户端获取
*WebSocket,但可能有点大材小用,看起来只能在客户端使用
*ReadableStream,根据next.js关于路由处理程序的文档,但无法使其工作(并且再次看起来不是服务器端的消耗品)
*Server Actions,使用revalidateTag,但它仍然是实验性的,我找不到一种简单的方法来告诉客户端何时刷新。
其他人是否遇到过此问题?如果是,你是如何处理的?next.js中是否有我可能忽略或误解的特性?
Thank you!:)
1条答案
按热度按时间t0ybt7op1#
很有趣的问题。换句话说,我觉得你只想有一个HTTP请求,逐步发送响应数据,但有客户端更新数据传入。
我认为你需要同时使用流,服务器端和客户端。
首先,您需要使用
ReadableStream
进行响应。代码有点太复杂,无法在这里分享,你可能想阅读GitHub上关于使用Route Handlers发送文件的讨论,它显示了不同的语法来实现这一目标。然后,在客户端,您可以使用类似的API获取此端点并使用流。fetch
Response
主体将是包含您的数据的ReadableStream
。您可以使用流事件处理程序来更新将重新呈现组件的状态变量。也许有
<Supspense />
或传入use
钩子的解决方案,但我认为你不需要,而不是使用promise客户端,你可以坚持使用事件处理程序。你需要弄清楚的一件事是如何使每个数据块“独立”(=一个完整的字符串,你可以调用
JSON.stringify
),可能有办法发送块大小沿着大小块(例如发送大小,然后字符串,然后大小等)。直到你发送-1关闭流)。这是理论上的在实践中,您可能会遇到的一个问题是,在Route Handlers中,您需要返回一个Response对象,然后只有Next.js才会开始触发HTTP响应。在API Route/Express端点中,您可以随时调用
res.send
,然后提供流。基本上,这意味着我不确定流的生命周期:你能在你从路由选择功能返回后继续输入流吗?可能是的,但值得一试。