浅析react-router@6版本中,Navigate组件重定向路由的使用
在版本为6的react-router和react-router-dom的使用中,与旧版有一点差别
- 没有
Switch和Redirect组件可以使用了 - 新增了一个
Routes组件,所有的Route组件都应该被Routes包裹,在Routes组件外使用Route将报错 Navigate组件不能写在Routes中,否则也会报错,也就是Routes组件只接收Route作为其子组件 下面的案例中,将Navigate组件定义在Route组件的element属性中,因为星号的匹配规则定义在最后,所以只有前面的路由都没有匹配时,才会命中(相当于兜底),而输出的Navigate组件做的事很简单,就是把错误的路由重定向到/aaa,来达到不会空渲染内容
import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { Routes, Route, Navigate, useRoutes } from "react-router";
const Child = () => {
return <div>1 aaa</div>;
};
const Child2 = () => {
return <div>2 bbb</div>;
};
export default function App() {
return (
<div>
<Router>
<Link to={"/aaa"}>to 1 aaa</Link>
<hr />
<Link to="/bbb">to 2 bbb</Link>
<hr />
<Link to="/ccc">to 3 cc</Link>
<hr />
<Routes>
<Route path="aaa" element={<Child />}></Route>
<Route path="bbb" element={<Child2 />}></Route>
<Route path="*" element={<Navigate to={"aaa"} replace />}></Route>
</Routes>
</Router>
</div>
);
}
这段代码,只能访问/aaa和/bbb,访问其他路由都会被捕获并重新跳转至/aaa,Navigate的replace如果不加的话则无法后退,因为/aaa-->/sdjfklsadj-->/aaa的场景下,错误的路由实际上会被记录到历史栈中,点击后退会退回到错误的路由,并又再次匹配到且跳转会/aaa,加了replace后历史栈则会变成/aaa->/aaa
延伸出的Navigate组件和useNavigate的实现,可以简单的看看源码
// Navigate组件的源码
function Navigate(_ref2) {
let {
to,
replace,
state
} = _ref2;
!useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of
// the router loaded. We can help them understand how to avoid that.
"<Navigate> may be used only in the context of a <Router> component.") : invariant(false) : void 0;
process.env.NODE_ENV !== "production" ? warning(!useContext(NavigationContext).static, "<Navigate> must not be used on the initial render in a <StaticRouter>. " + "This is a no-op, but you should modify your code so the <Navigate> is " + "only ever rendered in response to some user interaction or state change.") : void 0;
let navigate = useNavigate();
useEffect(() => {
navigate(to, {
replace,
state
});
});
return null;
}
其实干的事情不多,调用了名为useNavigate钩子返回了一个函数,函数的作用就是进行路由跳转,并将本来传给Navigate组件的props进行透传
// useNavigate部分源码
// 省略了一些上下文Context的代码,感兴趣可以自己去看看
let navigate = useCallback(function (to, options) {
if (options === void 0) {
options = {};
}
process.env.NODE_ENV !== "production" ? warning(activeRef.current, "You should call navigate() in a React.useEffect(), not when " + "your component is first rendered.") : void 0;
if (!activeRef.current) return;
if (typeof to === "number") {
navigator.go(to);
return;
}
let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname);
if (basename !== "/") {
path.pathname = joinPaths([basename, path.pathname]);
}
(!!options.replace ? navigator.replace : navigator.push)(path, options.state);
}, [basename, navigator, routePathnamesJson, locationPathname]);
return navigate;
- 而
navigate方法的定义也不会很绕,只是做了一些参数判断和转换,最后判断有没有传入replace属性,有则调用replace方法,没有则调用push方法 useCallback用于缓存函数,在[basename, navigator, routePathnamesJson, locationPathname]这些依赖项不变时,则始终是同一个函数对象- 在业务组件中涉及到路由的,可以使用
useNavigate来方便的操控路由,以前的版本可能使用的是(withRouter和useHistory)来达到相同的效果
const Child2 = (props) => {
const navigate = useNavigate();
const toA = () => {
navigate("/aaa");
};
return (
<div>
2 bbb
<button onClick={toA}>点击我也能去aaa</button>
</div>
);
};
示例中点击按钮后,绑定函数里调用了navigate("/xxx",{})亦可达到跳转路由效果
转载自:https://juejin.cn/post/7070309762759393317