loader

每个路由都可以定义一个“加载器”函数,在渲染路由元素之前为路由组件提供数据。

仅当使用数据路由时,此功能才有效。

createBrowserRouter([
  {
    path: '/',
    element: <App/>,
    loader: async () => {
      return fetch("https://httpbin.org/get")
    },
    children: [
      {
        path: 'home',
        element: <Home/>,
        loader: async () => {
          return fetch("https://httpbin.org/get")
        },
      }
    ]
  }
])

当用户在应用程序中进行导航时,将并行调用下一个匹配路由分支的加载器,并通过useLoaderData将其数据提供给组件。

params

params是从动态路由段中解析出来的参数,并传递给加载器:

路径中的:id被解析为相同名称的params.id

request

最常见的用例是创建 URL,并从中读取URLSearchParams

返回响应

loader中你可以返回任何你想要返回的数据,并从useLoaderData中得到它,甚至可以在loader中进行网络请求,例如:

React Router 会自动调用 response.json() ,因此组件在渲染时不需要解析它:

您也可以自己构建响应:

抛出异常

你可以在加载器中使用throw来跳出当前堆栈的,React Router 将渲染异常组件errorElement

延迟数据❓

上述事例中,我们通过sleep函数来模拟网络请求,我们会发现在慢速网络中,loader中的网络请求会导致导航的延迟跳转和视图的渲染。在网络非常慢的情况下,loader会直到请求得到响应后,才进行路由的跳转和视图的渲染,这将会导致页面的加载或跳转到该路由所需的时间被拉长。为了改善这一点:

  • 使用promise.all并行加载数据。

  • 添加全局过渡效果(有助于改善用户体验)。

  • 添加骨架屏(有助于改善用户体验)。

如果这些方法不起作用,我们不得不将网络请求从loader中移除,转而在组件中获取数据。

但是在大多数情况下仍然是不太理想的,特别是当你正在对路由组件进行代码拆分,原因有两个:

  1. 客户端将数据请求放在瀑布流上:文档 -> JavaScript -> 懒加载路由 -> 数据获取。

  2. 你的代码不能轻松地在组件获取和路由获取之间切换。

解决方案✅

React Router 利用 React18Suspense通过defer响应工具和<Await>组件或useAsyncValue钩子来进行数据获取。通过使用这些API,你可以解决以下两个问题:

  1. 你的网络请求不再在瀑布流上:文档 -> JavaScript -> 路由懒加载和获取数据(并行)。

  2. 你可以轻松地在回退渲染和等待数据之间切换。

使用SuspenseAwait包裹延迟渲染的视图,以便在渲染回退UI时候使用,使用defer包裹延迟的数据。

Drawing
Suspense和Await

或者你也可以使用useAsyncValue钩子,但你需要将代码分离到另一个组件中:

Drawing
使用useAsyncValue钩子

现在,我们不必等得到响应数据,在进行导航的跳转,而是在用户跳转新路由时立即获取数据。在网速较慢的情况下,此处会展示你所提供的loading状态。如果请求失败它会显示errorElement,请求成功则显示你所提供的视图。

此外,你还可以根据是否包含await关键字来切换某些内容是否被延迟。

为什么加载器返回的响应对象不再起作用?

当你使用defer时,你告诉了 React Router 立即加载页面,而不是延迟加载。页面在 Response 对象返回之前已经加载,因此响应不会像使用return fetch()一样自动处理。

你需要处理自己的 Response,并解析延迟的 Promise,而不是 Promise 实例。

甚至,我们可以嵌套多层网络请求。

最后更新于

这有帮助吗?