本篇内容主要翻译React RouterV6 FAQs内容,内容主要介绍了,v4/5版本与v6不同和常间的问题。
内容围绕下面问题展开:
1.v6中怎么使用withRouter,项目有类组件,想用withRouter有什么替代方案吗
v6中不再使用withRouter,如果类组件想用withRouter, 可以通过自定义一个wrapper函数,来解决这个问题
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component {...props} router={{ location, navigate, params }} />
);
}
return ComponentWithRouterProp;
}
2.为什么 有一个 element prop 而不是像原来定义为 render 或者 component
在 React Router v6 中,我们从使用 v5 的 <Route component> 和 <Route render> API 切换到 <Route element>。 这是为什么?
这种变化和传递props 有关,原来当需要想在路由组件中传递props时,需要借助render(props)或者高阶组件:
<Route path=":userId" component={Profile} />
<Route path=":userId"
render={routeProps => (
<Profile routeProps={routeProps} animate={true} />
)}/>
v5 中 component 只能接受字符串类型或组件,不能接受react 元素,但是v6可以;
<Route path=":userId" element={<Profile/>}/>
// 直接从元素中传递props
<Route path=":userId" element={<Profile animate={true}/>}
v5这样写会报下面这种错误:
另外,hooks的出现,也不需要withRouter 传递路由数据;
function DeepComponent(routeStuff) {
// got routeStuff, phew!}
export default withRouter(DeepComponent);
function DeepComponent() {
// oh right, same as anywhere else let navigate = useNavigate();
}
在 v6 中使用 element 属性的另一个重要原因是 是为嵌套路由保留的。 v5: Route 里的Children表示对应path下,渲染的组件;
v5:
<Switch>
<Route exact path="/" >
<Home/>
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
v6: Route里的Children表示嵌套,子路由组件,对应path渲染组件,始终写在element上面。
<Route path="/" element={<Root />}>
<Route path="contact" element={<Contact />} />
<Route path="dashboard" element={<Dashboard />} />
</Route>
3.如何添加404 Route 在v6版本中
v5:
<Route path="*">
<NoMatch/>
</Route>
v6:
<Route path="*" element={<NoMatch/> }/>
#### 为什么不渲染,我该怎么写?
v5 中, 组件component只是一个普通组件,就像if语句,当URL与其路径匹配时呈现,v6中,元素element实际不会渲染,只是用于配置。
let App = () => (
<div>
<MyRoute />
</div>
);
let MyRoute = ({ element, ...rest }) => {
return (
<Route path="/my-route" children={<p>Hello!</p>} />
);
};
V6这样写会出错,因为< MyRoute /> 在上没有配置任何path
let App = () => (
<Routes>
// 不会渲染,也就执行不到里面的Route
<MyRoute />
</Routes>
);
let MyRoute = () => {
// won't ever render because the path is down here return (
<Route path="/my-route" children={<p>Hello!</p>} />
);
};
// 这样正确
let App = () => (
// 在能够渲染位置,写Route
<div>
<Routes>
<Route path="/my-route" element={<MyRoute />} />
</Routes>
</div>
);
let MyRoute = () => {
return <p>Hello!</p>;
};
你也可以定义一个组件实现v5实现:
function MatchPath({ path, Comp }) {
let match = useMatch(path);
return match ? <Comp {...match} /> : null;
}
// Will match anywhere w/o needing to be in a `<Routes>`
<MatchPath path="/accounts/:id" Comp={Account} />;
4.在v6中使用嵌套路由该怎么写
v5中,你可以在你想渲染的任何地方使用 或 。v6中你同样可以做,但你需要使用,只写 将不会起作用。我们称这种为 后代 路由 Descendant
// somewhere up the tree
<Switch>
<Route path="/users" component={Users} />
</Switch>;
// and now deeper in the treefunction Users() {
return (
<div>
<h1>Users</h1>
<Switch>
<Route path="/users/account" component={Account} />
</Switch>
</div>
);
}
v6 实现需要使用Routes,在父级path使用*,及时它没有直接children组件; 你不需要写在 整个 child path了,你现在可以使用相对 path;
<Routes>
<Route path="/users/*" element={<Users />} />
</Routes>;
// and now deeper in the treefunction Users() {
return (
<div>
<h1>Users</h1>
<Routes>
<Route path="account" element={<Account />} />
</Routes>
</div>
);
}
5.正则表达式路由在v6替代方案
v6中删除了Regexp route path 正在表达式路由路径,其中有两个原因:
1、路由中的正则表达式路径对v6的路由排名匹配(ranked route matching),提出了很多新问题;
2、 去掉正在表达式路径,能够让我们去掉(path-to-regexp)依赖项,减少发送到用户浏览器包重量,
查看许多用例后,我们发现在没有直接正则表达式路径支持的情况下,我们仍然可以满足它们,因此我们进行了权衡,以显着减小捆绑包大小并避免围绕排名正则表达式路由的开放性问题。
-
匹配多静态值;
-
验证参数(eg:是否数字类型);
我们看到的一个非常常见的路线是匹配多种语言代码的正则表达式:
v5:
function App() {
return (
<Routes>
<Route path="en" element={<Lang lang="en" />} />
<Route path="es" element={<Lang lang="es" />} />
<Route path="fr" element={<Lang lang="fr" />} />
</Routes>
);
}
function Lang({ lang }) {
let translations = I81n[lang];
// ...}
另一个常见问题是校验参数
v5:
function App() {
return (
<Switch>
<Route path={/users/(\d+)/} component={User} />
</Switch>
);
}
function User({ params }) {
let id = params[0];
// ...}
v6 下 ,您必须自己使用匹配组件内的正则表达式做一些工作:
function App() {
return (
<Routes>
<Route path="/users/:id" element={<ValidateUser />} />
<Route path="/users/*" element={<NotFound />} />
</Routes>
);
}
function ValidateUser() {
let params = useParams();
let userId = params.id.match(/\d+/);
if (!userId) {
return <NotFound />;
}
return <User id={params.userId} />;
}
function User(props) {
let id = props.id;
// ...}
在 v5 中,如果正则表达式不匹配,则 将继续尝试匹配下一个路由:
function App() {
return (
<Switch>
<Route path={/users/(\d+)/} component={User} />
<Route path="/users/new" exact component={NewUser} />
<Route path="/users/inactive" exact component={InactiveUsers} />
<Route path="/users/*" component={NotFound} />
</Switch>
);
}
您可能会担心在 v6 版本中,您的其他路由不会在其 URL 处呈现,因为 :userId 路由可能会首先匹配。 但是,由于路线排名,情况并非如此。 “new”和“inactive”路由的排名会更高,因此会在各自的 URL 上呈现,
6. v6 不按照定义内容顺序匹配,由于有路由排名匹配(ranked route matching),它们会有各自对应权重, 事实上,如果您的路线排序不正确,v5 版本会出现各种各样的问题。 V6完全消除了这个问题。
function App() {
return (
<Routes>
<Route path="/users/:id" element={<ValidateUser />} />
<Route path="/users/new" element={<NewUser />} />
<Route path="/users/inactive" element={<InactiveUsers />} />
</Routes>
);
}
欢迎大家补充和指正~