【问题标题】:How to implement a "render-as-you-fetch" pattern for routes in React如何在 React 中为路由实现“render-as-you-fetch”模式
【发布时间】:2021-07-21 09:59:29
【问题描述】:

新的 Relay hooks API 将重点放在“render-as-you-fetch”的 React 模式上,到目前为止我真的很喜欢这个。 Relay 的 useQueryLoaderusePreloadedQuery 钩子让大部分时间实现这一点非常简单。

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

情况A:

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

情况乙:

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

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

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

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

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

方法一:

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

方法二:

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

方法3:

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

关于他们如何实现这一点,是否有人有任何其他想法或示例?

【问题讨论】:

    标签: reactjs relay react-suspense


    【解决方案1】:

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

    我理解他们的目标是:

    • 开始在渲染路径之前和之外加载您的查询
    • 在查询的同时开始加载您的组件 (code splitting)
    • 将预加载的查询引用传递到组件中

    在 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} />
    }
    

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

    • 可能先前已请求过惰性组件,因此应将其缓存
    • 数据提取位于渲染路径上

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

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

    TL;DR 使用中继 Issue Tracker 示例路由器。

    奖励:我已经 written a blog post 讲述了我理解这种模式的过程

    【讨论】:

    • 你好!我认为我有一个使用 React Router 的模式解决方案。我很确定它满足您为“即取即渲染”提出的所有要求。我很想知道您对解决方案的看法,特别是它是否确实克服了您上面描述的所有问题。
    • 注意,大概可以简化
    • 它肯定会同时开始加载组件和数据,并且不会阻塞另一个。我对它是否受到组件缓存问题的影响有点模糊。
    猜你喜欢
    • 2019-12-19
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 2012-03-14
    • 2019-02-09
    • 1970-01-01
    相关资源
    最近更新 更多