在服务器组件中顺序获取数据以改变相同的数据集(Next.js)

pgx2nnw8  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(94)

我是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!:)

t0ybt7op

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,然后提供流。
基本上,这意味着我不确定流的生命周期:你能在你从路由选择功能返回后继续输入流吗?可能是的,但值得一试。

相关问题