一文学会使用React-Router v6

news/2024/7/15 18:34:46 标签: react.js, javascript, 前端

文章目录

    • 基本格式
      • createBrowserRouter
        • Type Declaration
          • routes
          • basename
          • future and window ... 省略
      • <RouterProvider>
        • fall上述文本提到 `createBrowserRouter` 函数的一个特性:在非服务器端渲染应用程序时,当它挂载时会初始化所有匹配的路由加载器。在此期间,你可以提供 `fallbackElement`,以向用户显示应用程序正在加载的指示。
        • future ...
      • Components
        • <Await>

以下内容来自官方文档 https://reactrouter.com/en/main

基本格式

import * as React from "react";
import { createRoot } from "react-dom/client";
import {
  createBrowserRouter,
  RouterProvider,
  Route,
  Link,
} from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: (
      <div>
        <h1>Hello World</h1>
        <Link to="about">About Us</Link>
      </div>
    ),
  },
  {
    path: "about",
    element: <div>About</div>,
  },
]);

createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

createBrowserRouter

import * as React from "react";
import * as ReactDOM from "react-dom";
import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";

import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    loader: rootLoader,
    children: [
      {
        path: "team",
        element: <Team />,
        loader: teamLoader,
      },
    ],
  },
]);

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

Type Declaration
function createBrowserRouter(
  routes: RouteObject[],
  opts?: {
    basename?: string;
    future?: FutureConfig;
    hydrationData?: HydrationState;
    window?: Window;
  }
): RemixRouter;

routes

An array of Route objects with nested routes on the children property.

createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    loader: rootLoader,
    children: [
      {
        path: "events/:id",
        element: <Event />,
        loader: eventLoader,
      },
    ],
  },
]);

Route定义为:

const router = createBrowserRouter([
  {
    // it renders this element
    element: <Team />,

    // when the URL matches this segment
    path: "teams/:teamId",

    // with this data loaded before rendering
    loader: async ({ request, params }) => {
      return fetch(
        `/fake/api/teams/${params.teamId}.json`,
        { signal: request.signal }
      );
    },

    // performing this mutation when data is submitted to it
    action: async ({ request }) => {
      return updateFakeTeam(await request.formData());
    },

    // and renders this element in case something went wrong
    errorElement: <ErrorBoundary />,
  },
]);

RouteObject对象的声明为:

interface RouteObject {
  path?: string;
  index?: boolean;
  children?: React.ReactNode;
  caseSensitive?: boolean;
  id?: string;
  loader?: LoaderFunction;
  action?: ActionFunction;
  element?: React.ReactNode | null;
  Component?: React.ComponentType | null;
  errorElement?: React.ReactNode | null;
  ErrorBoundary?: React.ComponentType | null;
  handle?: RouteObject["handle"];
  shouldRevalidate?: ShouldRevalidateFunction;
  lazy?: LazyRouteFunction<RouteObject>;
}

对于Path:
动态路由参数, 可以在loader, action中通过params直接点出 或者使用useParams()钩子来获取该参数

<Route
  // this path will match URLs like
  // - /teams/hotspur
  // - /teams/real
  path="/teams/:teamId"
  // the matching param will be available to the loader
  loader={({ params }) => {
    console.log(params.teamId); // "hotspur"
  }}
  // and the action
  action={({ params }) => {}}
  element={<Team />}
/>;

// and the element through `useParams`
function Team() {
  let params = useParams();
  console.log(params.teamId); // "hotspur"
}

你可以定义多个动态参数 都可以通过 . 运算符获取到

<Route path="/c/:categoryId/p/:productId" />;
// both will be available
params.categoryId;
params.productId;

可选参数 通过在后面加上 ?

<Route
  // this path will match URLs like
  // - /categories
  // - /en/categories
  // - /fr/categories
  path="/:lang?/categories"
  // the matching param might be available to the loader
  loader={({ params }) => {
    console.log(params["lang"]); // "en"
  }}
  // and the action
  action={({ params }) => {}}
  element={<Categories />}
/>;

// and the element through `useParams`
function Categories() {
  let params = useParams();
  console.log(params.lang);
}

当然可以同时用动态参数和可选参数

模糊匹配所有的 * 参数

<Route
  // this path will match URLs like
  // - /files
  // - /files/one
  // - /files/one/two
  // - /files/one/two/three
  path="/files/*"
  // the matching param will be available to the loader
  loader={({ params }) => {
    console.log(params["*"]); // "one/two"
  }}
  // and the action
  action={({ params }) => {}}
  element={<Team />}
/>;

// and the element through `useParams`
function Team() {
  let params = useParams();
  console.log(params["*"]); // "one/two"
}

布局路由

<Route
  element={
    <div>
      <h1>Layout</h1>
      <Outlet />
    </div>
  }
>
  <Route path="/" element={<h2>Home</h2>} />
  <Route path="/about" element={<h2>About</h2>} />
</Route>

组件留出子路由要渲染的位置,仅此而已
index索引路由 url为父路径时显示的组件

<Route path="/teams" element={<Teams />}>
  <Route index element={<TeamsIndex />} />
  <Route path=":teamId" element={<Team />} />
</Route>
  • 外层的<Route>有一个路径path为"/teams",并渲染了一个组件<Teams />
  • 在这个路由内部,有另一个带有index属性的嵌套的<Route>。这意味着当路径匹配"/teams"时,组件<TeamsIndex />将被呈现到其父级路由的Outlet中(在这种情况下是<Teams />)。
  • 此外,还有另一个带有path=":teamId"的嵌套路由,表示当路径匹配"/teams/:teamId"时,将呈现组件<Team />

children: 嵌套路由
caseSensitive: 表示对路径中大小写敏感
Instructs the route to match case or not:

<Route caseSensitive path="/wEll-aCtuA11y" />
  • Will match “wEll-aCtuA11y”
  • Will not match “well-actua11y”

loader
每一个路由都能定义一个loader函数来为路由组件渲染之前提供数据

createBrowserRouter([
  {
    element: <Teams />,
    path: "teams",
    loader: async () => {
      return fakeDb.from("teams").select("*");
    },
    children: [
      {
        element: <Team />,
        path: ":teamId",
        loader: async ({ params }) => {
          return fetch(`/api/teams/${params.teamId}.json`);
        },
      },
    ],
  },
]);

通过 调用loader函数获得的数据可以通过 useLoaderData钩子拿到
useLoaderData: This hook provides the value returned from your route loader.

import {
  createBrowserRouter,
  RouterProvider,
  useLoaderData,
} from "react-router-dom";

function loader() {
  return fetchFakeAlbums();
}

export function Albums() {
  const albums = useLoaderData();
  // ...
}

const router = createBrowserRouter([
  {
    path: "/",
    loader: loader,
    element: <Albums />,
  },
]);

ReactDOM.createRoot(el).render(
  <RouterProvider router={router} />
);

在调用路由操作之后,数据将自动重新验证,并从加载器返回最新的结果。
请注意,useLoaderData 不会启动抓取。它只是读取 React Router 在内部管理的抓取结果,因此您不必担心在重新呈现的原因之外的情况下重新获取数据。
这也意味着在重新呈现之间返回的数据是稳定的,因此您可以安全地将其传递给 React hooks(如 useEffect)中的依赖数组。它只在再次调用加载器后(在执行操作或特定导航后)才会更改。在这些情况下,标识将更改(即使值没有更改)。
您可以在任何组件或任何自定义 hook 中使用此钩子,而不仅仅是在 Route 元素中。它将返回上下文中最近路由的数据。
params: 由url中解析的动态参数会放在params中并传递给loader函数

createBrowserRouter([
  {
    path: "/teams/:teamId",
    loader: ({ params }) => {
      return fakeGetTeam(params.teamId);
    },
  },
]);

request:

<a
  href={props.to}
  onClick={(event) => {
    event.preventDefault();
    navigate(props.to);
  }}
/>

React router阻止浏览器将请求request发送给server, 而是转给了loader函数

function loader({ request }) {
  const url = new URL(request.url);
  const searchTerm = url.searchParams.get("q");
  return searchProducts(searchTerm);
}

返回 response
可以返回fetch()发送异步请求(ajax, axios)的结果,也可以自定义返回结果

// an HTTP/REST API
function loader({ request }) {
  return fetch("/api/teams.json", {
    signal: request.signal,
  });
}

// or even a graphql endpoint
function loader({ request, params }) {
  return fetch("/_gql", {
    signal: request.signal,
    method: "post",
    body: JSON.stringify({
      query: gql`...`,
      params: params,
    }),
  });
}

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

react router会自动调用 response.json()

function SomeRoute() {
  const data = useLoaderData();
  // { some: "thing" }
}

在loader中抛出异常

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();
}

会渲染路由中定义的 errorElement组件
action: 进行写操作的函数

<Route
  path="/song/:songId/edit"
  element={<EditSong />}
  action={async ({ params, request }) => {
    let formData = await request.formData();
    return fakeUpdateSong(params.songId, formData);
  }}
  loader={({ params }) => {
    return fakeGetSong(params.songId);
  }}
/>

在应用发起一个非get请求时被调用 比如按钮点击

// forms
<Form method="post" action="/songs" />;
<fetcher.Form method="put" action="/songs/123/edit" />;

// imperative submissions
let submit = useSubmit();
submit(data, {
  method: "delete",
  action: "/songs/123",
});
fetcher.submit(data, {
  method: "patch",
  action: "/songs/123/edit",
});

element / Component: 要渲染的组件
errorElement / ErrorBoundary: 异常时渲染的组件
当在loader, action函数执行或者组件渲染时抛出异常, 就不会渲染element组件, 而是用error path的组件 errorElement渲染, 该异常可以用 useRouteError获取
lazy :

let routes = createRoutesFromElements(
  <Route path="/" element={<Layout />}>
    <Route path="a" lazy={() => import("./a")} />
    <Route path="b" lazy={() => import("./b")} />
  </Route>
);

basename

The basename of the app for situations where you can’t deploy to the root of the domain, but a sub directory.

createBrowserRouter(routes, {
  basename: "/app",
});

future and window … 省略

All data router objects are passed to this component to render your app and enable the rest of the data APIs

import {
  createBrowserRouter,
  RouterProvider,
} from "react-router-dom";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    children: [
      {
        path: "dashboard",
        element: <Dashboard />,
      },
      {
        path: "about",
        element: <About />,
      },
    ],
  },
]);

ReactDOM.createRoot(document.getElementById("root")).render(
  <RouterProvider
    router={router}
    fallbackElement={<BigSpinner />}
  />
);

fall上述文本提到 createBrowserRouter 函数的一个特性:在非服务器端渲染应用程序时,当它挂载时会初始化所有匹配的路由加载器。在此期间,你可以提供 fallbackElement,以向用户显示应用程序正在加载的指示。

这个特性的目的是在加载路由数据时,提供一个加载过程中的占位元素(fallback element),使用户在等待加载完成时能够看到一些界面反馈,而不是一片空白。
举例来说,你可以在 createBrowserRouter 中添加 fallbackElement 选项,像这样:

javascript">createBrowserRouter(routes, {
  fallbackElement: <div>Loading...</div>,
});

这样,当路由加载的过程中,页面将显示 “Loading…”,以告诉用户应用程序正在处理数据,避免用户在加载过程中感到不确定或无反馈。

future …

Components

import { Await, useLoaderData } from "react-router-dom";

function Book() {
  const { book, reviews } = useLoaderData();
  return (
    <div>
      <h1>{book.title}</h1>
      <p>{book.description}</p>
      <React.Suspense fallback={<ReviewsSkeleton />}>
        <Await
          resolve={reviews}
          errorElement={
            <div>Could not load reviews 😬</div>
          }
          children={(resolvedReviews) => (
            <Reviews items={resolvedReviews} />
          )}
        />
      </React.Suspense>
    </div>
  );
}

这段代码是一个使用 React Router 的 useLoaderDataAwait 组件的 React 组件。让我解释一下它的主要部分:

  1. useLoaderData 是 React Router 提供的一个 hook,用于获取路由加载器(loader)返回的数据。在这里,通过 useLoaderData 获取了 bookreviews
  2. return 中,首先渲染了 book 的标题和描述。
  3. 使用了 React 的 Suspense 组件,它接受 fallback 属性,指定在子组件加载过程中显示的占位元素。在这里,如果 reviews 尚未加载完成,将显示一个 ReviewsSkeleton 组件,用作加载占位符。
  4. 使用 Await 组件包装了 reviews 的加载。Await 组件接受 resolve 属性,指定需要等待的 Promise。如果 resolve Promise 处于 pending 状态,它会渲染 fallback 中的占位元素;如果 Promise 处于 resolved 状态,它会调用 children 函数,并将 resolved 的数据传递给它。
  5. children 函数中,渲染了一个 Reviews 组件,传递了从 reviews 获取的 resolved 数据。

总体来说,这段代码的目的是展示书籍的标题和描述,同时以异步方式加载书籍的评论。在加载评论的过程中,会显示一个加载中的占位符,确保用户在等待数据加载完成时能够看到一些界面反馈。如果加载评论失败,将显示一个错误提示。


http://www.niftyadmin.cn/n/5228744.html

相关文章

【java】记一次Java应用查询不到最新数据的问题

文章目录 项目第一次上线前&#xff0c;生产环境调试阶段&#xff0c;项目经理反馈在备机房所在环境验证时报错&#xff1a;id不存在。 我赶紧去排查&#xff0c;查看日志&#xff0c;发现日志里打印的id是旧数据记作A&#xff0c;拿着这个数据去调其他系统提示id不存在。 查看…

java+springboot物流管理系统设计与实现wl-ssmj+jsp

物流管理系统的开发和综合性的物流信息网站平台的建设。研究的重点是运输管理信息系统&#xff0e;本系统是一套基于运输作业流程的管理系统&#xff0c;该系统以运输任务、货品、商务三大线索设计开发。运输任务是该管理系统的核心&#xff0c;系统通过对运输任务中的接收、调…

8. 队列

队列(queue)是一种遵循先入先出规则的线性数据结构。顾名思义&#xff0c;队列模拟了排队现象&#xff0c;即新来的人不断加入队列的尾部&#xff0c;而位于队列头部的人逐个离开。 如下图所示&#xff0c;我们将队列的头部称为“队首”&#xff0c;尾部称为“队尾”&#xff…

k8s安装步骤

环境&#xff1a; 操作系统&#xff1a;win10 虚拟机&#xff1a;VMware linux发行版&#xff1a;CentOS7.9 CentOS镜像&#xff1a;CentOS-7-x86_64-DVD-2009 master和node节点通信的ip(master)&#xff1a; 192.168.29.164 0.检查配置 本次搭建的集群共三个节点&#xff0c;…

MySQL--InnoDB引擎

InnoDB引擎 逻辑存储引擎 表空间→段→区→页→行 Tablespace 表空间&#xff08;ibd文件&#xff09;&#xff1a;一个mysql实例可以对应多个表空间&#xff0c;用于存储记录、索引等数据Segment 段&#xff1a;段分为数据段、索引段、回滚段&#xff0c;InnoDB是索引组织表…

网络通信安全的坚固防线双向认证技术详解

目录 什么是双向认证 双向认证的工作原理 双向认证的实现方式 双向认证的重要性 双向认证的挑战 安全最佳实践 小结 什么是双向认证 双向认证&#xff0c;又称为双向身份验证或双向鉴别&#xff0c;是一种在通信双方之间建立信任关系的安全机制。在通信过程中&#xff0…

判断Xposed框架

判断Xposed框架 1: 根据包名判断 列举一些常用的应用判断是否安装,来判断. private static List<String> asList Arrays.asList("de.robv.android.xposed", "com.topjohnwu.magisk", "io.va.exposed", "org.meowcat.edxposed.man…

排序分析(Ordination analysis)及R实现

在生态学、统计学和生物学等领域&#xff0c;排序分析是一种用于探索和展示数据结构的多元统计技术。这种分析方法通过将多维数据集中的样本或变量映射到低维空间&#xff0c;以便更容易理解和可视化数据之间的关系。排序分析常用于研究物种组成、生态系统结构等生态学和生物学…