首页 > 解决方案 > 如何在 React 中为路由实现“render-as-you-fetch”模式

问题描述

新的 Relay hooks API 将重点放在“render-as-you-fetch”的 React 模式上,到目前为止我真的很喜欢这个。中继useQueryLoaderusePreloadedQuery钩子在大多数情况下使实现这一点非常简单。

然而,我正在努力寻找一个关于如何在路由方面实现这种模式的好模式。我发现有两种典型情况使这难以实现。

情况一:

  1. 用户加载主页 ( example.com/)
  2. 用户深入应用程序树的一部分 ( example.com/settings/user/security/authentication)
  3. 然后他们点击一个链接将他们带到他们应用程序中完全不相关的部分(example.com/blog/post-1

情况乙:

  1. 用户使用 URL 栏转到应用程序的某个部分,而不是使用链接 ( example.com/blog/post-1)

example.com/blog/post-1对于这些示例,有两种结果,用户要么通过嵌套子组件或直接通过 URL转到路由 ( )。所以我们为这条路线获取数据的方式必须支持这两种方法。

我假设我们希望尽早触发此路由的获取,因此当用户单击链接或我们在页面加载时检测到此路由时。

我可以想到三个想法来实现这一点:

  1. 改用一个fetch-then-render模式(比如 Relay 的useLazyLoadQuery钩子)
  2. 存储一个函数(比如在上下文中),并让该路由的所有链接在其onClick方法useEffect中调用该函数,如果没有加载数据,或者查询的引用已过时,还有一个用于该路由的调用该函数
  3. 使用render-as-you-fetch函数但实现它们以fetch-then-render支持

方法一:

这违背了render-as-you-fetch模式的目的,但它是一种简单的出路,更有可能是一种“更清洁”的方式来实现为路由获取数据。

方法二:

在实践中,我发现这很难实现。通常,指向路由的链接与组件渲染路由所在的组件树的一部分断开连接。并且使用上下文意味着我必须loadData为特定路线管理不同的功能(当涉及变量等时这可能会很棘手)。

方法3:

这就是我目前一直在做的事情。在实践中,它通常会导致能够将加载数据函数传递给附近的组件,但是如果断开连接的组件、URL 或页面重新加载等访问路由,则组件会退回到调用加载useEffect钩子中的数据函数。

有没有人对他们如何实现这一点有任何其他想法或例子?

标签: reactjsrelayreact-suspense

解决方案


我也一直在努力理解这一点。我发现这些资源特别有用:

我了解他们旨在为您实现的目标是:

  • 在渲染路径之前和之外开始加载查询
  • 在查询的同时开始加载你的组件(代码拆分
  • 将预加载的查询引用传递给组件

在 Relay 演示中解决它的方法是通过他们称之为“入口点”的东西。这些被大量集成到他们的路由器中(您可以在问题跟踪器示例中看到这一点)。它们包括以下组件:

  1. 路由定义(例如/items
  2. 一个惰性组件定义(例如() => import('./Items')
  3. 启动查询加载的函数(例如() => preloadQuery(...)

当路由器匹配一个新路径时,它开始加载惰性组件的过程,以及查询。然后它将这两个传递到一个上下文对象中,以由它们的RouterRenderer.

至于如何实现这一点,似乎最重要的规则是:

  1. 不要在组件内部请求数据,在路由或事件级别请求它
  2. 确保同时请求数据和惰性组件

一个简单的解决方案似乎是创建一个负责收集数据的组件,然后渲染相应的组件。就像是:

const LazyItemDetails = React.lazy(() => import('./ItemDetails'))

export function ItemEntrypoint() {
   const match = useMatch()
   const relayEnvironment = useEnvironment()
   const queryRef = loadQuery<ItemDetailsQuery>(relayEnvironment, ItemDetailsQuery, { itemId: match.itemId })
   
   return <LazyItemDetails queryRef={queryRef} />
}

但是,问题跟踪器示例添加解决方案的潜在问题是:

  • 惰性组件可能之前已经被请求过,因此应该被缓存
  • 数据获取位于渲染路径上

相反,问题跟踪器解决方案使用一个路由器来缓存组件,并在匹配路由的同时获取数据(通过监听历史更改事件)。如果您对维护自己的路由器感到满意,您可以在自己的代码中使用此路由器。

就现成的解决方案而言,似乎没有一个路由器可以实现 fetch-as-you-render 所需的模式。

TL;DR使用中继问题跟踪器示例路由器。

奖励:写了一篇关于我理解这种模式的过程的博客文章


推荐阅读