检索数据服务器端并使用 Next.js 保存在上下文中

Bob*_*erg 10 javascript next.js

我希望能够从服务器端的 API 检索数据,并将其加载到 React 上下文中,以使其可用于我的应用程序中的任何组件。我尝试了各种方法,但似乎没有什么能让我完全按照自己的意愿去做。我尝试过的一些事情包括:

getServerSideProps - 这允许我在服务器端检索数据,但只存在于页面组件中,所以如果我希望在每个页面上都可以使用它,并且我不知道我的用户将登陆哪个页面,我需要将此逻辑添加到每个页面。

_app.js 中的 getInitialProps - 我可以将其添加到 _app.js 组件中,该组件将在服务器端运行,并且可以通过上下文提供程序使其对所有组件可用,但问题是它在每个页面上运行,甚至在浏览客户端时。我希望能够一次且仅一次调用 API,但这似乎不允许。

_document.js 中的 getInitialProps - 我可以将其添加到 _document.js 组件中,该组件仅在服务器上运行,这似乎解决了为每个页面调用它的问题,但我无法弄清楚如何将它存储在 React 上下文中从那里。事实上,我似乎无法弄清楚如何在任何地方访问这些数据。它看起来像在_document.js getInitialProps被称为在_app.js getInitialProps,所以我不知道如果我能在_document.js当我在_app.js使用我产生的价值从getInitialProps。

如果我在客户端上调用 API,有多种方法可以使此工作正常进行,但这不适用于我的用例,因为当客户端使用 API 中的数据更新时,它会导致内容闪烁。

有没有人想出解决这个用例的方法?

Eri*_*rel 35

Next.js 13+ 答案

Next.js 实验性应用程序文件夹现在完美地涵盖了这个用例。

通过 React 上下文将值从 RSC 传递到客户端组件

  1. 在 React Server Component (RSC) 布局中获取数据
  2. 在此布局中,使用此值渲染 React 上下文
  3. 根据需要在您的应用程序中使用此上下文

当然,您可以在树中更下方的 RSC 中设置此上下文,例如,如果您仅在某个页面或页面的某个组件上需要它。

在这个现实生活中的示例中,我在 RSC 页面中获取调查定义,并通过上下文将其传递给客户端代码。我的上下文提供程序公开了一个类型化的可重用挂钩。

React 服务器组件的示例代码(此处为页面):

// /app/referer/page.tsx (Server Component)
import { headers } from 'next/headers'
export default async function Page() {
  const headersList = headers()
  const referer = headersList.get('referer')
  return (
    // we set a CLIENT context,
    // based on the SERVER context that we got via headers()
    <RefererProvider value={referer}>
      <InteractiveReferer />
    </RefererProvider>
  )
}
Run Code Online (Sandbox Code Playgroud)

客户端上下文的示例代码(不要忘记"use client"指令):

// /components/InteractiveReferer.tsx (Client Component)
// use "client"
export const InteractiveReferer = () => {
  // this context has been initialized by a parent RSC
  // but we can access it as expected in a Client Component
  const referer = useRefererContext()
  const [clickedReferer, setClickedReferer] = useState(false)
  return (
    <button
      onCLick={() => {
        setClickedReferer(true)
      }}
    >
      Referer: {referer}
    </button>
  )
}
Run Code Online (Sandbox Code Playgroud)

通过缓存将值从 RSC 传递到其他 RSC

Next.js 应用程序文件夹的另一个新颖之处是,现在服务器端代码不再局限于页面级别getServerSideProps。嵌套组件也可以是服务器组件并触发服务器调用。因此,有时您可能不仅想设置客户端上下文,还想设置一种服务器端上下文,其范围仅限于当前请求。

我在本文中详细描述了这个用例。综上所述,您可以使用 React 18cache函数(在撰写本文时尚未记录)来实现此目标。

我制作了此模式的开源演示,可以通过以下代码示例进行总结:

import { cache } from "react";

export const getServerContext = cache(() => ({
    // a dummy context for the demonstration
    createdAt: Date.now().toString(),
    calledByLayout: false,
    calledByPage: false,
    calledByNested: false
}))
Run Code Online (Sandbox Code Playgroud)

您可以直接改变返回值以在此上下文中存储新信息。

关于命名的注释:我一直使用术语“服务器上下文”来指定存储在cache充当上下文的数据。这是因为它是“客户端”上下文的服务器端等效项。然而,“请求缓存”可能更合适,因为“服务器上下文”在 Next.js 和 React 的未来版本中可能用于其他目的(在 RSC 和客户端组件之间共享数据)。

获取/设置和布局

对于 RSC,“缓存”函数允许实现“缓存 getter”,您并不真正传递值,而是调用该函数来获取值。由于缓存,该值实际上只检索一次,您的数据库或 API 不会因请求而过载。

在极少数情况下,无法使用此方法,即如果您想从页面 props 中获取值(而不是从 API/数据库中获取数据)并将其传递给子项而无需进行 props 钻探。

server -only-context包实现了一种替代的“获取/设置”模式,用于模仿 RSC 的上下文。您可以在页面中设置一个值,并在组件中访问它。请注意,您无法保证布局在页面之前呈现(由于客户端导航),因此您无法从布局设置值并从页面获取它。希望这种模式永远不会被严格需要。


Nic*_*ick 7

在 Next.js 中,没有本机函数 a) 从 API 检索数据,b) 在服务器上执行,c) 使其在每个页面上都可用,以及 d) 仅在用户访问的第一个页面上查询 API。

正如你已经发现了,getInitialProps并且getServerSideProps将运行每次访问该网页。

但是,我们可以让它发挥作用。

如果您在初始加载之前需要数据

  1. 使用getInitialProps_app.js从API获取数据
  2. 将数据加载到_app.js文件内的 React 上下文中,使其在页面之间保持不变
  3. 当浏览器获取数据时,创建一个 cookie。
  4. 在随后的页面加载中getInitialProps,检查是否有 cookie。如果是这样,请不要检索数据。

在 Next.js 项目中有一个相当流行的库,称为nookies来帮助处理 cookie。

如果您可以加载页面,则获取数据

使用getInitialPropsin存在性能成本_app.js:您永远无法创建完全静态的页面。那是因为getInitialProps必须在每个页面加载时运行。

如果可以在页面加载后获取数据,请添加API 路由。然后,在上下文提供程序中,用于useEffect获取数据。