如何在 React 中实现路由的“渲染即取”模式

Cha*_*wis 0 relay reactjs react-suspense

新的 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)

情况B:

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

这些示例有两种结果,即用户example.com/blog/post-1通过嵌套子组件或直接通过 URL 访问路由 ( )。因此,我们为此路线获取数据的方式必须支持这两种方法。

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

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

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

方法一:

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

方法2:

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

方法三:

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

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

小智 5

关于这个主题的更新,React Router v6 最近引入了对路由加载器的支持,允许基于路由预加载 Relay 查询。

例子:

import { StrictMode, Suspense } from "react";
import ReactDOM from "react-dom/client";
import {
  createBrowserRouter,
  Link,
  RouterProvider,
  useLoaderData,
} from "react-router-dom";
import graphql from "babel-plugin-relay/macro";
import {
  loadQuery,
  PreloadedQuery,
  RelayEnvironmentProvider,
  usePreloadedQuery,
} from "react-relay";
import { environment } from "./environment";
import { srcGetCurrentUserQuery } from "./__generated__/srcGetCurrentUserQuery.graphql";

const getCurrentUser = graphql`
  query srcGetCurrentUserQuery {
    viewer {
      id
      fullname
    }
  }
`;



const Test = () => {
  const data = usePreloadedQuery(getCurrentUser, preloadedQuery);
  const preloadedQuery = useLoaderData() as PreloadedQuery<srcGetCurrentUserQuery>;

  return (
    <Suspense fallback={<>Loading...</>}>
      <Viewer preloadedQuery={preloadedQuery} />
    </Suspense>
  );
};

const router = createBrowserRouter([
  {
    element: (
      <>
        {"index"} <br /> <Link to={"/test"}>Go test</Link>
      </>
    ),
    path: "/",
  },
  {
    element: <Test />,
    path: "test",
    loader: async () => {
      return Promise.resolve(
        loadQuery<srcGetCurrentUserQuery>(environment, getCurrentUser, {})
      );
    },
  },
]);

ReactDOM.createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <RelayEnvironmentProvider environment={environment}>
      <RouterProvider router={router} />
    </RelayEnvironmentProvider>
  </StrictMode>
);

Run Code Online (Sandbox Code Playgroud)

有关 React Router 加载器的更多信息:https ://reactrouter.com/en/main/route/loader