Skip to content

React Router 进阶技巧

React Router v6 升级提示

本文内容已于 2024 年更新,全面适配 React Router v6。如果你仍在使用 v4/v5 版本,请注意:withRouter<Switch>exact 等核心 API 在 v6 中已被完全废弃。全面拥抱 Hooks 和 <Routes> 是 v6 的核心哲学。

在 TypeScript 中使用编程式导航

在 v6 之前,我们通常需要使用 withRouter 高阶组件将 history 注入到类组件的 Props 中。但在 v6 中,withRouter 已经被移除,官方推荐使用纯函数组件和 useNavigate Hook。

下面是一个在 TypeScript 中使用 useNavigate 的现代例子:

typescript
import { useNavigate } from 'react-router-dom';

interface NavigationProps {
  name: string;
}

const Navigation: React.FC<NavigationProps> = ({ name }) => {
  const navigate = useNavigate();

  const toggleRoute = (path: string) => {
    // 编程式导航,取代了原本的 history.push(path)
    navigate(path);
  };

  return (
    <div>
      {/* 导航栏视图 */}
    </div>
  );
};

export default Navigation;

匹配机制:v6 移除了 exact 和 strict

在 React Router v6 中,路由匹配逻辑被彻底重写:

  1. 默认精准匹配:不再需要写 exact。所有的 <Route path="/a"> 都会精准匹配 /a
  2. 嵌套与模糊匹配:如果你希望匹配该路径下的所有子路由,必须显式在末尾添加通配符,即 path="/a/*"
  3. 最佳匹配机制:路由的顺序不再重要(不需要像 v5 那样小心翼翼地把具体路径放在前面)。v6 拥有智能的打分机制,会自动选择最匹配的路由。

路由配置化:useRoutes 取代 react-router-config

在 v5 中,我们习惯使用 react-router-config 或手写遍历 <Switch> 的形式来实现路由配置树。

在 v6 中,官方直接内置了路由配置化的 Hook:useRoutes,它让嵌套路由的声明变得像 JSON 一样简单。

typescript
import { useRoutes, RouteObject } from "react-router-dom";

const routes: RouteObject[] = [
  {
    path: "/",
    element: <HomePage /> // 注意 v6 使用 element 而不是 component,且传入 JSX
  },
  {
    path: "/user",
    element: <UserPage />,
    children: [
      { path: "profile", element: <Profile /> } // 嵌套子路由
    ]
  }
];

function App() {
  const element = useRoutes(routes);
  return element;
}

路由变化响应

在 VueJS 技术栈中,vue-router 提供路由响应的钩子函数,例如:beforeEachafterEach 等等。

但在 React 中,react-router 并不提供这种全局的钩子函数。如果有顶部导航栏,不同页面切换时,高亮不同的标签,我们该如何监听路由的变化呢?

在早期的类组件时代,开发者经常依赖 componentWillReceiveProps(已被废弃)或者 getDerivedStateFromProps 配合 withRouter 来监听。

在现代 React(Hooks 时代)和 React Router v6 中,这件事变得极其优雅:直接使用 useLocation 配合 useEffect 即可。

typescript
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";

const Navigation: React.FC = () => {
  const location = useLocation();
  const [selectedPath, setSelectedPath] = useState<string>("");

  useEffect(() => {
    // 每次 location.pathname 发生变化时,这个 useEffect 都会触发
    console.log("路由跳转到了:", location.pathname);
    
    // 我们可以在这里更新导航栏的高亮状态,或者发送 PV 埋点数据
    setSelectedPath(location.pathname);
    
  }, [location.pathname]); // 依赖项为当前的路径

  return <div>导航栏选中信息:{selectedPath}</div>;
};

export default Navigation;

优势:

  1. 抛弃了臃肿的类组件和生命周期。
  2. 数据流清晰:依赖 location.pathname,当其改变时自动触发副作用。
  3. 如果只想渲染视图而不涉及副作用请求,甚至连 useEffect 都不需要,直接在 Render 函数中取用 location.pathname 即可。

Released under the MIT License.