React-router 总结
react-router
应用一般划分为三大板块:
- Layouts: 内嵌子页面
- Pages: 页面
- Components: 复用组件
pages vs components
- page 也是一个 component,但是它是与具体的路由绑定的
- component 一般是抽象出来方便复用的组件,
Link, a
- Link 拦截浏览器默认行为,但是仅限于左键单击, 其他类型的点击(右键。。)不受影响。
- history.pushState() 来改变页面 url,触发 hashChange 事件,被 router 捕捉来处理跳转
- change history, location and render new matches in routes
-
的“跳转”行为只会触发相匹配的
对应的页面内容更新,而不会刷新整个页面。
NavLink
- NavLink 是特殊的 Link,会记录当前的 Link 是否是 active 状态,体现在 class 之中
- NavLink active 匹配多个路由, ./a/b/c 会匹配 /a, /a/b …
endproperty: 它将确保当其后代路径匹配时,此组件不会被匹配为“活动”, 当且仅当在其根目录下时才表现为 active
获取页面 url
- 当前页面 url,使用 useLocation 或者 useNavigation
//1
const location = useLocation();
// 获取当前页面的路径
location.pathname;
//2
const navigation = useNavigation();
navigation.location.pathname;
- 下一步跳转的页面
const navigation = useNavigation();
const nextUrl = navigation.formAction;
解析路由所提供的 params
使用 useParams
解析 request 的 params
使用 URL 方法
// 获取 query
new URL(request.url).searchParams.get("query");
// 获取 pathname
new URL(request.url).pathname;
useNavigate vs redirect
useNavigate 作为 hook 仅能在组件顶部和自定义 hook 内,而redirect则可以广泛运用于 loader 和 action 之中
useNavigation
useNavigation 返回整个 navigate 行为状态的对象, 详见useNavigation
const navigation = useNavigation();
navigation.state; // "idle" | "submitting" | "loading"
navigation.formData; //是否有提交内容
navigatin.location; //下一个 location 会是哪里
navigation.formAction === navigation.location.pathname
? "reloading"
: "redirecting"; // 判断是重载还是跳转
常用场景: 渲染 button
<button disabled={navigation.state === "submitting"}>
{navigation.state === "submitting" ? "Logging in..." : "Log in"}
</button>
保存当前 url 的信息至下一个页面
在跳转前保存本页面 url 的信息,比如 ?query= 或者 filter 则使用
Link
<Link state={query: ...}>
useNavigate
const navigate = useNavigate();
navigate("/new-route", { state: { key: "value" } });
等到了新页面,使用 useLocation 来取得先前储存的 state 信息,在返回
useLoaderData
loader属性从路由接收参数,比如 params(:id..),和 requestuseLoaderData不使用 defer 方法时,等待 promise resolved,返回 json. 如果使用 defer 方法,返回一个包裹 promise 的 json 对象,然后直接进入渲染环节,在 ui 部分使用Await去等待 resolved
对比 useEffect
- useEffect 缺点
- 客户端负责获取数据并渲染,则由服务端提供的初始的 html 将不包含数据;
- useEffect 直接请求数据会引发网络瀑布,父组件->子组件会反反复复;
- useEffect 直接请求数据无法预加载或者缓存,组件卸载再挂载将重复请求数据;
- 需要编写大量样板代码,并且分别处理状态,错误处理等
- useLoaderData 优点
- 预加载数据成功后才会渲染,统一进行错误处理
- 客户端缓存,避免重复请求,类似的还有
React Query,useSWR等等 - 并行请求数据避免网络瀑布阻塞
- 避免大量样本代码
dataLoader 与 defer
当使用 dataLoader 时候,都是先 load 数据完成再渲染页面,这可能会等待较长时间,由于不使用 useEffect,无法独立的处理状态,因此有了 defer,Await,Suspense 的运用。
loader 函数中不再等待 promise 状态结束而是直接返回一个对象,其中键值对中,值为 promise 类型
return defer({ data: getDatas() });
在需要 defer loading 的组件之上套一层Suspense组件来提供 status 信息。
const loaderData = useLoaderData()
// fallback to show "Loading..." when promise not returned yet
<Suspense fallback={...}>
<Await resolve={loaderData...}>
{...}
</Await>
</Suspense>
outlet && useOutletContext
嵌套子组件获取父组件中的信息
登录权限 Login
- 路由 loader 函数从 localstorage 读取登录状态, 如果为 false, 则 redirect
export async function requireAuth(request) {
const pathname = new URL(request.url).pathname;
const isLoggedIn = localStorage.getItem("loggedin");
if (!isLoggedIn) {
throw redirect(
`/login?message=You must log in first.&redirectTo=${pathname}`,
);
}
}
- 跳转登录页面, loader 提取 message 并显示
export function loader({ request }) {
return new URL(request.url).searchParams.get("message");
}
- 提交表单 =>触发 action, 核实后 redirect 原先的 path
export async function action({ request }) {
const formData = await request.formData();
const email = formData.get('email');
const password = formData.get('password');
const pathname = new URL(request.url).searchParams.get('redirectTo') || '/host';
try {
const data = await loginUser({ email, password });
localStorage.setItem('loggedin', true);
return redirect(pathname);
} catch (err) {
return err.message;
}
- true, 无事发生,正常跳转
lazy
<Route lazy={() => import("./....jsx")}
在路由层面定义懒加载和缓存数据,返回一个 promise 或者 thenable 对象
Form
-
React Router 将 HTML 表单导航模拟为数据突变原型,符合 JavaScript 大爆发之前的网络开发。它为您提供了客户端呈现应用程序的用户体验功能和 “老式 “网络模型的简洁性。虽然 HTML 表单对于一些网络开发人员来说并不熟悉,但它实际上是在浏览器中进行导航,就像点击链接一样。唯一的区别在于请求:链接只能更改 URL,而表单还可以更改请求方法(GET 与 POST)和请求主体(POST 表单数据)。如果没有客户端路由,浏览器会自动序列化表单数据,并将其作为 POST 的请求体和 GET 的 URLSearchParams 发送到服务器。React Router 也会做同样的事情,只不过它不是将请求发送到服务器,而是使用客户端路由将其发送到路由操作。
- to 如果是相对路径,则默认是解析路径根据 parent route, 即`relative="route"`.设置为`relateive="path"` 则是根据 path 解析。