前端
PythonJava运维数据库
React-Router(v6)
React-Router(v6)
  • Routers
    • createBrowserRouter
    • createHashRouter
    • createMemoryRouter
    • createStaticHandler
    • createStaticRouter
    • RouterProvider
    • StaticRouterProvider
  • Router 组件
    • BrowserRouter
    • HashRouter
    • MemoryRouter
    • NativeRouter
    • Router
    • StaticRouter
  • Route
    • Route
    • action
    • errorElement
    • hydrateFallbackElement
    • lazy
    • loader
    • shouldRevalidate
  • 组件
    • Await
    • Form
    • Link
    • Link(RN)
    • NavLink
    • Navigate
    • Outlet
    • Route
    • Routes
    • ScrollRestoration
  • 钩子函数
    • useActionData
    • useAsyncError
    • useAsyncValue
    • useBeforeUnload
    • useBlocker
    • useFetcher
    • useFetchers
    • useFormAction
    • useHref
    • useinRouterContext
    • useLinkcLickHandler
    • useLinkPressHandler
    • useLoaderData
    • useLocation
    • useMatch
    • useMatches
    • useNavigate
    • useNavigation
    • useNavigationType
    • useOutlet
    • useOutletContext
    • useParams
    • unstable_usePrompt
    • useResolvedPath
    • useRevalidator
    • useRouteError
    • useRouteLoaderData
    • useRoutes
    • useSearchParams
    • useSearchParams (RN)
    • useSubmit
    • unstable_useViewTransitionState
  • Utilities
    • Fetch 工具类
      • json
      • redirect
      • redirectDocument
      • replace
    • 其他工具类
      • createRoutesFromChildren
      • createRoutesFromElements
      • createSearchParams
      • defer
      • generatePath
      • isRouteErrorResponse
      • Location
      • matchPath
      • matchRoutes
      • renderMatches
      • resolvePath
  • 🥇Routers
    • Router
    • Route 配置
      • lazy
      • action
      • shouldRevalidate
      • loader
由 GitBook 提供支持
在本页
  • params
  • request
  • 返回响应
  • 抛出异常
  • 延迟数据❓
  • 解决方案✅
  • 为什么加载器返回的响应对象不再起作用?

这有帮助吗?

  1. Routers
  2. Route 配置

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将其数据提供给组件。

const router = createBrowserRouter([
  {
    path: "/",
    element: <App/>,
    loader: () => ({username: "zs", age: 20}),
    children: [
      {
        path: "hoem",
        element: <Home/>,
        loader: () => ({username: "ls", age: 21}),
      }
    ]
  }
])

function Home() {
  const loaderData = useLoaderData()

  return <div>name: {loaderData.username}</div>
}

params

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

createBrowserRouter([
  {
    path: '/',
    element: <App/>,
    loader: async () => {
      return fetch("https://httpbin.org/get")
    },
    children: [
      {
        path: 'team/:id',
        element: <Team/>,
        loader: async ({params}) => {
          return fetch(`https://httpbin.org/get?id=${params.id}`)
        },
      }
    ]
  }
])

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

request

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

export async function loader({request, context}: LoaderFunctionArgs) {
  const url = new URL(request.url)
  const params = url.searchParams.get('id')
  return { id: params }
}

返回响应

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

export async function loader({request, context}: LoaderFunctionArgs) {
  return fetch("https://httpbin.org/get?name=zs&age=20")
}

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

function SomeRoute() {
  const loaderData = useLoaderData();
  console.log(loaderData.args) // {age: '20', name: 'zs'}
  
  return <div>{loaderData.args.name}---{loaderData.args.age}</div>
}

您也可以自己构建响应:

function loader({ request, params }) {
  const data = { some: "thing" };
  return new Response(JSON.stringify(data), {
    status: 200,
    headers: {
      "Content-Type": "application/json; utf-8",
    },
  });
}

抛出异常

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

function loader({ request, params }) {
  const res = await fetch(`/api/properties/${params.id}`);
  if (res.status === 404) {
    throw new Response("Not Found", { status: 404 });
  }
  return res.json();
}

延迟数据❓

function sleep(ms: number): Promise<IUser> {
  return new Promise(resolve => setTimeout(() => resolve({
    name: "zs", age: 20
  }), ms));
}

export async function loader({params, request, context}: LoaderFunctionArgs) {
  return {
    user: await sleep(1000)
  }
}

export const Component = () => {
  const data = useLoaderData() as { user: IUser }
  return (
    <div>
      <h1>welcome</h1>
      <p>
        Your name is {data.user.name} and you are {data.user.age} years old.
      </p>
    </div>
  );
};

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

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

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

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

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

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

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

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

解决方案✅

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

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

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

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

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

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

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

return defer({
    // not deferred
    user1: await sleep(1000),
    // deferred
    user2: sleep(1000)
})

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

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

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

async function loader() {
    return defer({
        data: fetch(url).then(res => res.json())
    })
}

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

function sleep(ms: number): Promise<IUser> {
  return new Promise(resolve => setTimeout(() => resolve({
    name: "zs", age: 20
  }), ms));
}

function sleep1(ms: number): Promise<IUser> {
  return new Promise(resolve => setTimeout(() => resolve({
    name: "ls", age: 21
  }), ms));
}

export async function loader({params, request, context}: LoaderFunctionArgs) {
  const user = sleep(1000).then(async res => {
    return await sleep1(1000)
  })
  return defer({
    user
  })
}

上一页shouldRevalidate

最后更新于2年前

这有帮助吗?

🥇
Suspense和Await
使用useAsyncValue钩子
Drawing
Drawing