- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我们是 袋鼠云数栈 UED 团队 ,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值.
本文作者:景明 。
目前公司产品有关 react 的工具版本普遍较低,其中 react router 版本为 3.x(是的,没有看错,3.x 的版本)。而最新的 react router 已经到了 6.x 版本.
为了能够跟上路由的脚步,也为了使用 router 相关的 hooks 函数,一次必不可少的升级由此到来! 。
react-touter 6.x 版本,只对 react 和 react-dom 版本有要求,我们的项目满足条件.
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
确定使用 react-router-dom: 6.11.1 为目标升级版本。是的,跳过了v4/v5 版本,直接上 v6 一步到位 。
在 v6 版本,分为了 3 个包(PS:兼容包不算) 。
从 V6 开始,只需要使用 react-router-dom 即可,不会直接使用 react-router.
对应的是组件引用的变更,如下:
// v3 版本
import { Link } from 'react-router'
// v6 版本后
import { Link } from 'react-router-dom';
Route 类型定义 。
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; // 在 loader / action 过程中抛出异常展示
ErrorBoundary?: React.ComponentType | null;
handle?: RouteObject["handle"];
lazy?: LazyRouteFunction<RouteObject>;
}
v6 中使用简化的路径格式。 <Route path> 在 v6 中仅支持 2 种占位符:动态 :id 参数和 * 通配符。通配符 * 只能用在路径的末尾,不能用在中间.
// 有效地址
/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*
// 无效地址
/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*
判断该路由是否为索引路由(默认的子路由).
<Route path="/teams" element={<Teams />}>
<Route index element={<TeamsIndex />} />
<Route path=":teamId" element={<Team />} />
</Route>
设置了 index 的 route 不允许存在子路由 。
在路由组件渲染前执行并传递数据,组件可通过 useLoaderData 获取 loader 的返回值.
createBrowserRouter([
{
element: <Teams />,
path: "/",
// 打开配置将造成死循环,因为 /view 也会触发 / 的 loader
// loader: async () => {
// return redirect('/view');
// },
children: [
{
element: <Team />,
path: "view",
loader: async ({ params }) => {
return fetch(`/api/view/${params.id}`);
},
},
],
},
]);
需要注意的是,loader 是并行触发,匹配多个 route,这些 route 上如果都存在 loader,都会执行.
想要针对特定的路由,可以采用如下写法:
export const loader = ({ request }) => {
if (new URL(request.url).pathname === "/") {
return redirect("/view");
}
return null;
};
// element?: React.ReactNode | null;
<Route path="/a" element={<Properties />} />
// Component?: React.ComponentType | null;
<Route path="/a" Component={Properties} />
与 v3 相比,v6 是大写开头的 Component。 v6 更推荐采用 element 的方式,可以非常方便的传递 props 。
在 v6 版本支持中心化配置,可以通过 createHashRouter 进行配置。 使用如下,结构就是 route 的定义:
export const getRoutes = createHashRouter([
{
path: '/',
Component: AuthLayout,
children: [
...commonRouteConfig,
{
Component: SideLayout,
children: [
{
path: 'metaDataCenter',
Component: MetaDataCenter,
},
{
path: 'metaDataSearch',
Component: MetaDataSearch,
},
{
path: 'metaDataDetails',
Component: MetaDataDetails,
},
{
path: 'dataSourceDetails',
Component: MetaDataDetails,
},
}
]
}
]
引入如下:
import { RouterProvider } from 'react-router-dom';
<RouterProvider router={getRoutes} />
与 v3 相比:
在组件内使用:
// This is a React Router v6 app
import {
BrowserRouter,
Routes,
Route,
Link,
} from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users/*" element={<Users />} />
</Routes>
</BrowserRouter>
);
}
function Users() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Routes>
<Route path=":id" element={<UserProfile />} />
<Route path="me" element={<OwnUserProfile />} />
</Routes>
</div>
);
}
<Route path>
和 <Link to>
是相对父元素的地址。
// 动态路由权重,比如 /foo/:id
const dynamicSegmentValue = 3;
// 索引路由权重,也就是加了 index 为 true 属性的路由
const indexRouteValue = 2;
// 空路由权重,当一段路径值为空时匹配,只有最后的路径以 / 结尾才会用到它
const emptySegmentValue = 1;
// 静态路由权重
const staticSegmentValue = 10;
// 路由通配符权重,为负的,代表当我们写 * 时实际会降低权重
const splatPenalty = -2;
declare function useNavigate(): NavigateFunction;
interface NavigateFunction {
(
to: To,
options?: {
replace?: boolean;
state?: any;
relative?: RelativeRoutingType;
}
): void;
(delta: number): void;
}
在组件内原本采用 history 进行跳转,在 V6 修改成使用 navigate 进行跳转.
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
如果需要替换当前位置而不是将新位置推送到历史堆栈,请使用 navigate(to, { replace: true }) 。 如果你需要增加状态,请使用 navigate(to, { state }) 。
如果当前正在使用 history 中的 go、goBack 或 goForward 来向后和向前导航,则还应该将它们替换为 navigate 的第一个数字参数,表示在历史堆栈中移动指针的位置 。
// v3 -> v6
go(-2)} -> navigate(-2)
goBack -> navigate(-1)
goForward -> navigate(1)
go(2) -> navigate(2)
declare function Navigate(props: NavigateProps): null;
interface NavigateProps {
to: To;
replace?: boolean;
state?: any;
relative?: RelativeRoutingType;
}
如果你更喜欢使用声明式 API 进行导航( v5 的 Redirect),v6 提供了一个 Navigate 组件。像这样使用它:
import { Navigate } from "react-router-dom";
function App() {
return <Navigate to="/home" replace state={state} />;
}
注意:v6 默认使用push逻辑,你可以通过 replaceProps 来更改它.
history 库是 v6 的直接依赖项,在大多数情况下不需要直接导入或使用它。应该使用 useNavigate 钩子进行所有导航.
然而在非 tsx 中,如 redux 、 ajax 函数中。我们是无法使用react hooks的.
这个时候可以使用 location ,或者 history 进行跳转.
history.push("/home");
history.push("/home?the=query", { some: "state" });
history.push(
{
pathname: "/home",
search: "?the=query",
},
{
some: state,
}
);
history.go(-1);
history.back();
采用 window.location 对象进行跳转.
window.location.hash = '/'
// V3
type Location = {
pathname: Pathname;
search: Search;
query: Query;
state: LocationState;
action: Action;
key: LocationKey;
};
// V6
type Location = {
pathname: Pathname;
search: Search;
state: LocationState;
key: LocationKey;
};
在 v3 中,我们可以通过 location.query 进行 Url 的参数获取或设置,而在 v6 中是不支持的.
在使用 useNavigate 时,接收一个完整的 pathname,如:/user?name=admin 。
在我们自己的工具库 dt-utils 中,新增 getUrlPathname 方法用来生成 pathname.
getUrlPathname(pathname: string, queryParams?: {}): string
// example
DtUtils.getUrlPathname('/metaDataSearch', { metaType, search })
获取时使用 getParameterByName 进行获取单个 query param。也新增了 getUrlQueryParams 方法获取所有的 query params 。
// getParameterByName(name: string, url?: string): string | null
// 需要注意 getParameterByName 返回的是 null。在多数情况下,需要转成 undefined
const standardId = DtUtils.getParameterByName('standardId') || undefined;
// getQueryParams(url: string): Record<string, string>
const query = DtUtils.getUrlQueryParams(location.search);
通过 useParams 获取到路由上的参数.
import * as React from 'react';
import { Routes, Route, useParams } from 'react-router-dom';
function ProfilePage() {
// Get the userId param from the URL.
let { userId } = useParams();
// ...
}
function App() {
return (
<Routes>
<Route path="users">
<Route path=":userId" element={<ProfilePage />} />
</Route>
</Routes>
);
}
在进行路由跳转时可以通过传递 state 状态进行传参.
// route 传递
<Route path="/element" element={<Navigate to="/" state={{ id: 1 }} />} />
// link 传递
<Link to="/home" state={state} />
// 跳转传递
navigate('/about', {
state: {
id: 1
}
})
// 获取 state
export default function App() {
// 通过 location 中的 state 获取
let location = useLocation();
const id = location.state.id
return (
<div className="App">
<header>首页</header>
<p>我的id是:{id}</p>
</div>
);
}
可通过 useOutletContext 获取 outlet 传入的信息.
function Parent() {
const [count, setCount] = React.useState(0);
return <Outlet context={[count, setCount]} />;
}
import { useOutletContext } from "react-router-dom";
function Child() {
const [count, setCount] = useOutletContext();
const increment = () => setCount((c) => c + 1);
return <button onClick={increment}>{count}</button>;
}
在 v3 中使用 setRouteLeaveHook 进行路由的拦截,在 v6 被移除了.
this.props.router.setRouteLeaveHook(this.props.route, () => {
if (!this.state.finishRule) {
return '规则还未生效,是否确认取消?';
}
return true;
});
在 V6 中采用 usePrompt 进行组件跳转拦截.
需要注意的是,由于 usePrompt 在各浏览器中交互可能不一致.
目前可拦截前进,后退,正常跳转.
刷新页面不可拦截.
/**
* Wrapper around useBlocker to show a window.confirm prompt to users instead
* of building a custom UI with useBlocker.
*
* Warning: This has *a lot of rough edges* and behaves very differently (and
* very incorrectly in some cases) across browsers if user click addition
* back/forward navigations while the confirm is open. Use at your own risk.
*/
declare function usePrompt({ when, message }: {
when: boolean;
message: string;
}): void;
export { usePrompt as unstable_usePrompt };
针对这个功能,封装了一个 usePrompt 。
import { unstable_usePrompt } from 'react-router-dom';
import useSyncState from '../useSyncState';
/**
* 拦截路由改变
* @param {boolean} [initWhen = true] 是否弹框
* @param {string} [message = ''] 弹框内容
* @returns {(state: boolean, callback?: (state: boolean) => void) => void}
*/
const usePrompt = (initWhen = true, message = '') => {
const [when, setWhen] = useSyncState(initWhen);
unstable_usePrompt({ when, message });
return setWhen;
};
export default usePrompt;
// example
import usePrompt from 'dt-common/src/components/usePrompt';
const EditClassRule = (props: EditClassRuleProps) => {
const setWhen = usePrompt(
checkAuthority('DATASECURITY_DATACLASSIFICATION_CLASSIFICATIONSETTING'),
'规则还未生效,是否确认取消?'
);
return (
<EditClassRuleContent {...(props as EditClassRuleContentProps)} setFinishRule={setWhen} />
);
};
在 V3 中 router 会给每一个匹配命中的组件注入相关的 router props 。
注入 props 在 V6 是没有的.
v3 中的 withRouter 将 react-router 的 history、location、match 三个对象传入props对象上.
在 v6 上 withRouter 这个方法也是没有的.
在 v6 中,提供了大量 hooks 用于获取信息.
获取 location 的 useLocation。获取路由 params 的 useParams,获取 navigate 实例的 useNavigate 等.
实现了一个 withRouter 的高阶函数,用于注入这 3 个 props。 这里没有直接传入,采用 router 对象的原因是:
import React from 'react';
import {
useNavigate,
useParams,
useLocation,
Params,
NavigateFunction,
Location,
} from 'react-router-dom';
export interface RouterInstance {
router: {
params: Readonly<Params<string>>;
navigate: NavigateFunction;
location: Location;
};
}
function withRouter<P extends RouterInstance = any, S = any>(
Component: typeof React.Component<P, S>
) {
return (props: P) => {
const params = useParams();
const navigate = useNavigate();
const location = useLocation();
const router: RouterInstance['router'] = {
params,
navigate,
location,
};
return <Component {...props} router={router} />;
};
}
export default withRouter;
// example
export default withRouter<IProps, IState>(Sidebar);
该篇文章中主要记录了,我们项目从 react-router@3.x 升级到 @6.x 遇到的一些问题以及相关的解决方案,也简单讲解了 v3 与 v6 的部分差异,欢迎大家讨论提出相关的问题~ 。
欢迎关注【袋鼠云数栈UED团队】~ 袋鼠云数栈UED团队持续为广大开发者分享技术成果,相继参与开源了欢迎star 。
最后此篇关于ReactRouter@3.x升级到@6.x的实施方案的文章就讲到这里了,如果你想了解更多关于ReactRouter@3.x升级到@6.x的实施方案的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
问题故障解决记录 -- Java RMI Connection refused to host: x.x.x.x .... 在学习JavaRMI时,我遇到了以下情况 问题原因:可
我正在玩 Rank-N-type 并尝试输入 x x .但我发现这两个函数可以以相同的方式输入,这很不直观。 f :: (forall a b. a -> b) -> c f x = x x g ::
这个问题已经有答案了: How do you compare two version Strings in Java? (31 个回答) 已关闭 8 年前。 有谁知道如何在Java中比较两个版本字符串
这个问题已经有答案了: How do the post increment (i++) and pre increment (++i) operators work in Java? (14 个回答)
下面是带有 -n 和 -r 选项的 netstat 命令的输出,其中目标字段显示压缩地址 (127.1/16)。我想知道 netstat 命令是否有任何方法或选项可以显示整个目标 IP (127.1.
我知道要证明 : (¬ ∀ x, p x) → (∃ x, ¬ p x) 证明是: theorem : (¬ ∀ x, p x) → (∃ x, ¬ p x) := begin intro n
x * x 如何通过将其存储在“auto 变量”中来更改?我认为它应该仍然是相同的,并且我的测试表明类型、大小和值显然都是相同的。 但即使 x * x == (xx = x * x) 也是错误的。什么
假设,我们这样表达: someIQueryable.Where(x => x.SomeBoolProperty) someIQueryable.Where(x => !x.SomeBoolProper
我有一个字符串 1234X5678 我使用这个正则表达式来匹配模式 .X|..X|X. 我得到了 34X 问题是为什么我没有得到 4X 或 X5? 为什么正则表达式选择执行第二种模式? 最佳答案 这里
我的一个 friend 在面试时遇到了这个问题 找到使该函数返回真值的 x 值 function f(x) { return (x++ !== x) && (x++ === x); } 面试官
这个问题在这里已经有了答案: 10年前关闭。 Possible Duplicate: Isn't it easier to work with foo when it is represented b
我是 android 的新手,我一直在练习开发一个针对 2.2 版本的应用程序,我需要帮助了解如何将我的应用程序扩展到其他版本,即 1.x、2.3.x、3 .x 和 4.x.x,以及一些针对屏幕分辨率
为什么案例 1 给我们 :error: TypeError: x is undefined on line... //case 1 var x; x.push(x); console.log(x);
代码优先: # CASE 01 def test1(x): x += x print x l = [100] test1(l) print l CASE01 输出: [100, 100
我正在努力温习我的大计算。如果我有将所有项目移至 'i' 2 个空格右侧的函数,我有一个如下所示的公式: (n -1) + (n - 2) + (n - 3) ... (n - n) 第一次迭代我必须
给定 IP 字符串(如 x.x.x.x/x),我如何或将如何计算 IP 的范围最常见的情况可能是 198.162.1.1/24但可以是任何东西,因为法律允许的任何东西。 我要带198.162.1.1/
在我作为初学者努力编写干净的 Javascript 代码时,我最近阅读了 this article当我偶然发现这一段时,关于 JavaScript 中的命名空间: The code at the ve
我正在编写一个脚本,我希望避免污染 DOM 的其余部分,它将是一个用于收集一些基本访问者分析数据的第 3 方脚本。 我通常使用以下内容创建一个伪“命名空间”: var x = x || {}; 我正在
我尝试运行我的test_container_services.py套件,但遇到了以下问题: docker.errors.APIError:500服务器错误:内部服务器错误(“ b'{” message
是否存在这两个 if 语句会产生不同结果的情况? if(x as X != null) { // Do something } if(x is X) { // Do something } 编
我是一名优秀的程序员,十分优秀!