likes
comments
collection
share

浅析react-router@6版本中,Navigate组件重定向路由的使用

作者站长头像
站长
· 阅读数 18

在版本为6的react-router和react-router-dom的使用中,与旧版有一点差别

  1. 没有SwitchRedirect组件可以使用了
  2. 新增了一个Routes组件,所有的Route组件都应该被Routes包裹,在Routes组件外使用Route将报错
  3. 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,访问其他路由都会被捕获并重新跳转至/aaaNavigatereplace如果不加的话则无法后退,因为/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来方便的操控路由,以前的版本可能使用的是(withRouteruseHistory)来达到相同的效果
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
评论
请登录