每次打开 React Router 官方文档,都会有惊吓,API又又又变了!这次看看有什么更新。
(资料图片)
好家伙!这是我认知中的 React Router 吗?
我2022年3月开发《联机桌游合集》时,在用 6.2 版本,那时候 v6 跟 v5 v4 相比,API 已经发生了比较大的变化,但我认可这些变化。
现在看完 6.4 版本文档, 我想吐槽。 我的核心观点是:React Router 6.4 不再是纯粹的路由组件了,它耦合了数据获取逻辑。
下面本文 客观介绍: React Router 6.4 引入的新功能 Data API,并在最后给 主观结论。
createXXXXRouter
API在 React Router 6.4 中,新增了 3 个 createXXXXRouter
API,用于支持 data API:
createBrowserRouter
createMemoryRouter
createHashRouter
也就是说,如果你不用这3个API,而是像v6.0
-v6.3
一样,直接使用
等下面几个API,那么你享受不到 data API。
createXXXXRouter
用法必须结合
一起使用。可以看到,它使用一个配置,定义路由。
import * as React from "react";import * as ReactDOM from "react-dom";import { createBrowserRouter, RouterProvider,} from "react-router-dom";const router = createBrowserRouter([ { path: "/", element: , children: [ { path: "team", element: , }, ], },]);ReactDOM.createRoot(document.getElementById("root")).render( );
当然,如果你喜欢用JSX语法定义路由,像
一样:
} />
React Router 6.4 也提供了JSX配置,参考createRoutesFromElements,它有另外一个名字叫createRoutesFromChildren。
const router = createBrowserRouter( createRoutesFromElements( }> } /> } /> ));
的变化当你使用createXXXXRouter
和
时,你就可以使用 Data API。
说了这么多,什么是 Data API 呢?
其实就是允许你把「数据获取逻辑」写到路由定义中。每当路由切换到那里时,会自动获取数据。
我们从
的变化就可以看出,它新增了3个相关的属性:
loader 属性loader属性传入一个函数(允许是 async function),每次渲染「该路由对应的element」前执行函数。在「该路由对应的element」内,可以使用 hookuseLoaderData
(下文会介绍)来获取这个函数的返回值(通常是http请求的response)。
{ // loaders can be async functions const res = await fetch("/api/user.json", { signal: request.signal, }); const user = await res.json(); return user; }} element={}/>
loader属性传入的函数,允许有2个参数:
params: 如果Route中包含参数(例如path是/user/:userId
,参数就是:userId
,可以通过params.userId获取到路由参数的值)。request: 是 Web 规范中,Fetch API 的 Request,代表一个请求。注意:这里指的不是你在 loader 内部发的 fetch 请求,而是当用户路由到当前路径时,发出的“请求”(其实在Single-Page App中,router已经拦截了这个真实的请求,只有Multi-Page App中才会有这个请求),这里是 React Router 6.4 为了方便开发者获取当前路径信息提供的参数,他们按照 Web规范,制造了一个假的 request。你可以通过 request
方便的获取当前页面的参数: { const url = new URL(request.url); const searchTerm = url.searchParams.get("q"); return searchProducts(searchTerm); }}/>
不要这个 request 参数行吗?不行,因为如果你用window.location
获取的信息是当前最新的值,如果用户快速的点击按钮,让页面路由到A,并立马路由到B,这时候路由A对应的Route的loader获取window.location
时,就可能拿到错误的值。
注意,传递 request,还有个好处,它有个 request.signal,当用户快速的点击按钮,让页面路由到A,并立马路由到B,页面A的loader的请求应该被取消掉,可以通过 signal 实现,如下:
{ return fetch("/api/teams.json", { signal: request.signal, }); }}/>
函数的返回值,将可以在element中通过hook useLoaderData
(下文会介绍)来获取。你返回什么,它就拿到什么。
但是 React Router 官方建议,返回一个 Web规范 中的 Fetch API 的 Response。
你可以直接 return fetch(url, config);
,也可以自己构造一个假的 Response:
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 提供的 json 来构造:
import { json } from "react-router-dom";function loader({ request, params }) { const data = { some: "thing" }; return json(data, { status: 200 });}//...
redirect
在 loader 中,可能校验后需要重定向,React Router 不建议你用 useNavigation 完成,建议直接在 loader 中直接 return redirect,跳转到新的网址。
import { redirect } from "react-router-dom";const loader = async () => { const user = await getUser(); if (!user) { return redirect("/login"); }};
如果数据获取失败,或者其它任何原因,你认为不能让 Route 对应的 element 正常渲染了,你都可以在 loader 中 throw 异常。这时候,「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();}//...
注意:你可以抛出任何异常,都可以在 errorElement 内通过 hook useRouteError
来获取到异常。
但是,React Router 官方建议你 throw Response:
} errorElement={} loader={async ({ params }) => { const res = await fetch(`/api/properties/${params.id}`); if (res.status === 404) { throw new Response("Not Found", { status: 404 }); } const home = res.json(); return { home }; }}/>
你依然可以用 React Router 提供的 json 方法,方便的构造个 Response:
throw json( { message: "email is required" }, { status: 400 },);
element 属性这个不是新属性,即
2.3.1 内部可用 useLoaderData
获取 loader 返回值
注意,如果 loader 返回值是 Response,并且 Response 的 Content Type 是 application/json,React Router 内部会自动调用 .json() 方法,开发者不必写 .json() 了。
function Albums() { const albums = useLoaderData(); return {albums};}const router = createBrowserRouter([ { path: "/", loader: fetch("/api"), element: , },]);ReactDOM.createRoot(el).render( );
2.3.2 内部可调用 useRouteLoaderData
获取 其它 Route 的 loader 返回值
React 组件可以嵌套,
也可以嵌套,这时可以通过该 hook 获取其它
的 loader 的返回值。当然,你需要提供 id。
定义路由时:
createBrowserRouter([ { path: "/", loader: () => fetchUser(), element: , id: "root", children: [ { path: "jobs/:jobId", loader: loadJob, element: , }, ], },]);
内部调用这个hook时:
const user = useRouteLoaderData("root");
2.4
errorElement 属性
当 loader 内抛出异常,
就不渲染它的 element 了,而是渲染它的 errorElement。
2.4.1 异常可以冒泡
是可以嵌套的,每一层都可以定义 errorElement,异常发生后,会找到最近的 errorElement,并渲染它,然后停止冒泡。
2.4.2 内部可用 useRouteError
获取异常
在 errorElement 内,可用 useRouteError
获取异常。
const error = useRouteError();
2.4.3 内部可用 isRouteErrorResponse
判断异常类型
React Router 给了一个函数 isRouteErrorResponse
,帮你在开发 errorElement 时,可以判断当前异常是否是 Response 异常。因为 Response 异常 通常是开发者自己抛出的,是可以展示原因的(包括后端接口返回错误码和错误提示文案,也可在这里处理)。其它异常,通常是未知的,就直接展示兜底的报错文案即可。
function RootBoundary() { const error = useRouteError(); if (isRouteErrorResponse(error)) { if (error.status === 404) { return This page doesn"t exist!; } if (error.status === 503) { return Looks like our API is down; } } return Something went wrong;}
2.5
action 属性
它很像 laoder,你看:
它也有2个参数:params 和 request。定义跟 loader 一样。你可以 return 任何东西,同样 React Router 建议你 return Response。你也可以 return redirect,实现重定向。在element内,你可以用hook useActionData
获取 action 返回值。(类似 useLoaderData
)不同点在于,它们执行时机不同:
loader 是用户通过 GET 导航至某路由时,执行的。action 是用户提交 form 时,做 POST PUT DELETE 等操作时,执行的。以前写过
溧阳:投入470万元 全力支持乡村“复兴少年宫”建设
2021年,溧阳市被列为全国乡村复兴少年宫50家试点县市之一。为进一步拓展农村未成年人身心健康全面发展...
2022-08-29 14:28:14
江苏经济报
郴州安仁文旅项目集中开工 总投资1000万元
3月16日,安仁县举行文旅项目集中开工活动,县委书记王洪灿在开工仪式上宣布:湘南起义旧址群——朱毛井...
2022-03-20 15:40:46
2022年郴州计划重点推进文旅项目101个 总投资354亿元
3月16日,我市举行全市文旅项目和城市大提质大融城项目集中开工仪式,市委书记吴巨培宣布项目开工。郴州...
2022-03-20 15:39:41
宿州泗县深入推进文旅融合发展 擦亮城市品牌
近年来,泗县以争创安徽省文化旅游名县为目标,深入推进文旅融合发展,努力擦亮水韵泗州 运河名城城市...
2022-03-20 15:38:59
汽车零部件产业“领头羊” 锦州力争一季度“开门红”
3月16日,记者从锦州汽车零部件产业的领头羊——锦州万得集团获悉,今年前两个月,企业订单充足,正铆足...
2022-03-20 15:37:41
油价或有望冲击“九元”大关 宁波新能源汽车市场如何
新一轮国内成品油调价窗口于3月17日24时开启,油价或有望冲击九元大关。前一天晚上11点,鄞州区不少加油...
2022-03-20 15:34:38
从水塘到“云”端 全国最大高邮鸭养殖基地实现智慧养殖
随着新一代数字技术的蓬勃发展,以新兴技术推动现代化新农村建设正成为助力乡村振兴的重要手段。1个人能...
2022-03-20 15:33:17
淡季不忘引流 京郊民宿市场有望迎来回暖
旅游淡季中的京郊民宿有望成为市场中最先复苏的板块。3月17日,北京商报记者调查发现,虽然正值旅游淡季...
2022-03-20 15:32:01
镇江乡村一二三产业融合发展 闯出“镇江之路”
从烹饪江鲜河豚的个体小饭店到规模化的江岛乡村旅游产业集群,从白兔草莓丁庄葡萄的单个农户种植到茅山...
2022-03-20 15:31:11
总投资30亿元 盐城东台8个重大产业项目相继开工
总投资30亿元的精密电子元器件项目、同益电子项目,总投资10亿元的金利美精密组件项目、天永智能设备项...
2022-03-20 15:30:13
x
广告
全球新资讯:赖系绿委林俊宪:人事改组再拖会有不利流言
有关“内阁”改组议题,是否陈建仁搭配前桃园市长郑文灿,民进党亲近赖清德“立委”林俊宪今天受访说,...
信息:禹州市城市发展中心扎实做好春节期间安全生产工作
【来源:许昌市城管局_县域城管】为切实做好春节期间安全生产工作,及时排查消除安全隐患,坚决防范各类...
世界今日报丨超级电容板块1月12日涨0.59%,华锋股份领涨,主力资金净流入289.3万元
1月12日超级电容板块较上一交易日上涨0 59%,华锋股份(002806)领涨。当日上证指数报收于3163 45,上涨...
观点:探访广东湛江高桥红树林自然保护区
中新网广州1月12日电 (唐贵江 祝桂峰)“添绿又增金。”日前,记者
今日精选:1月12日正和集团油品报价暂稳
产品1月11日1月12日涨跌单位:元 吨-10 柴油730073000元 吨92 汽油850085000元 吨95 汽油862086200元 吨1月12日正和集
x
广告
Copyright ? 2015-2022 华北粮油网版权所有 备案号:粤ICP备18023326号-8 联系邮箱:855 729 8@qq.com