你可能需要一份完整的react-router-dom 6 升级指南

1,210 阅读2分钟
  1. 升级react-router-dom包,可以去掉react-router,@types/react-router, @types/react-router-dom的依赖
  yarn add react-router-dom
  yarn remove react-router @types/react-router @types/react-router-dom
  1. <Routes>包裹<Route>而不是<Switch>
// Change this:
<Switch>
  <Route path="about-us">
    ...
  </Route>
</Switch>

// to this:
<Routes>
  <Route path="about-us">
    ...
  </Route>
</Routes>
  1. 包裹的组件将不能以children或者component的形式传入,换成element传入,
  2. exact属性将不再需要,react-router-dom会通过更智能的算法匹配最适合的路由
  3. component属性将以组件形式传入而不是表达式
// Change this:
<Route exact path="/about" component={About} />
// Or this:
<Route exact path="/about">
  <About />
</Route>
// To this:
<Route path="/about" element={<About />} />
  1. <Route>的嵌套规则和路由匹配规则发生改变,子<Route>中的path将变成相对路径
// v6 只支持:参数匹配和 * 通配符

/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*

//正则匹配不再支持

/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*
  1. 单独出现时必须被包裹
  2. 新增用来确定子路由的插入位置
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  Outlet,
} from "react-router-dom";

// This is a React Route v5 app

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/">
          <Home />
        </Route>
        <Route path="/users">
          <Users />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

function Users() {
  return (
    <div>
      <nav>
        <Link to="me">My Profile</Link>
      </nav>
      <Switch>
        <Route path="/users/me">
          <OwnUserProfile />
        </Route>
        <Route path="/users/:id">
          <UserProfile />
        </Route>
      </Switch>
    </div>
  );
}
// This is a React Router v6 app
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="users" element={<Users />}>
          <Route path="me" element={<OwnUserProfile />} />
          <Route path=":id" element={<UserProfile />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

function Users() {
  return (
    <div>
      <nav>
        <Link to="me">My Profile</Link>
      </nav>
      <Outlet />
    </div>
  );
}

//等价于
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="me" element={<OwnUserProfile />} />
        <Route path=":id" element={<UserProfile />} />
      </Routes>
    </div>
  );
}
  1. <Redirect>替换成<Navigate>
// Change this:
<Switch>
  <Redirect from="about" to="about-us" />
</Switch>

// to this:
<Routes>
  <Route path="about" element={<Navigate to="about-us" replace/>} />
</Routes>

// Change this:
<Redirect to="about" />
<Redirect to="home" push />

// to this:
<Navigate to="about" replace />
<Navigate to="home" />
  1. <Link>传入state的方式改变
import { Link } from "react-router-dom";

// Change this:
<Link to={{ pathname: "/home", state: state }} />

// to this:
<Link to="/home" state={state} />
  1. useHistory的hook被去除掉了,替换成了useNavigate
// This is a React Router v5 app, Change this:
import { useHistory } from "react-router-dom";

function App() {
  const { go, goBack, goForward } = useHistory();

  return (
    <>
      <button onClick={() => go(-2)}>
        Go 2 pages back
      </button>
      <button onClick={goBack}>Go back</button>
      <button onClick={goForward}>Go forward</button>
      <button onClick={() => go(2)}>
        Go 2 pages forward
      </button>
    </>
  );
}
// To this
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";

function App() {
  const navigate = useNavigate();
  return (
    <>
      <button onClick={() => navigate(-2)}>
        Go 2 pages back
      </button>
      <button onClick={() => navigate(-1)}>Go back</button>
      <button onClick={() => navigate(1)}>
        Go forward
      </button>
      <button onClick={() => navigate(2)}>
        Go 2 pages forward
      </button>
    </>
  );
}
  1. 因为useHistory和Prompt被去掉,官方没有给出history.block的替代方法,如下用usePrompt和useBlock的hook用来替代history.block, 且usePrompt中抽象了离开页面弹框的逻辑,使用起来更简单
import { useContext, useEffect, useCallback } from "react";
import { UNSAFE_NavigationContext as NavigationContext } from "react-router-dom";
import { Button } from "antd";
import { useNavigate } from "react-router-dom";
/**
 * Blocks all navigation attempts. This is useful for preventing the page from
 * changing until some condition is met, like saving form data.
 *
 * @param  blocker
 * @param  when
 * @see https://reactrouter.com/api/useBlocker
 */
export function useBlocker(blocker, when = true) {
  const { navigator } = useContext(NavigationContext);

  useEffect(() => {
    if (!when) return;

    const unblock = navigator.block((tx) => {
      const autoUnblockingTx = {
        ...tx,
        retry() {
          // Automatically unblock the transition so it can play all the way
          // through before retrying it. TODO: Figure out how to re-enable
          // this block if the transition is cancelled for some reason.
          unblock();
          tx.retry();
        },
      };

      blocker(autoUnblockingTx);
    });

    return unblock;
  }, [navigator, blocker, when]);
}
/**
 * Prompts the user with an Alert before they leave the current screen.
 *
 * @param  when
 * @param  callback
 * @param  message
 */
export function usePrompt(
  when = true,
  callback?: (
    location: Transition["location"],
    action: Transition["action"]
  ) => boolean,
  message?: string
) {
  const blocker = useCallback(
    (tx: Transition) => {
      const result = isUndefined(callback)
        ? true
        : callback(tx.location, tx.action);
      if (result) {
        useWarnConfirm({ title: message, unsaved: !message }, () => {
          tx.retry();
        })();
        //一个自定义的弹框hook
      } else {
        tx.retry();
      }
    },
    [message]
  );
  return useBlocker(blocker, when);
}


export default () => {
  const navigate = useNavigate();
  const when = true; //当when=true时才会执行判断弹框等逻辑
  usePrompt(when, (location, action) => {
    console.log(location, action);
    //一些判断location,action的逻辑,返回true时才会出现弹框
    return true;
  },"确定要离开页面吗",);
  // callback也可不传,默认会提示弹框
  const handleLeave = () => {
    navigate("/");
  };
  return <Button onClick={handleLeave}>点击离开页面</Button>;
};
  1. NavLink中去掉了activeClassName属性,className将接收一个执行函数,通过判断isActive来确定是否命中路由
<NavLink
  to="/messages"
- className="nav-link"
- activeClassName="activated"
+ className={({ isActive }) => "nav-link" + (isActive ? " activated" : "")}
>
  Messages
</NavLink>